تصف Brush في Compose كيفية رسم عنصر على الشاشة، وتحدّد الألوان التي يتم رسمها في مساحة الرسم (مثل دائرة، مربّع أو مسار). تتوفّر بعض الفُرش المضمّنة المفيدة للرسم،
مثل LinearGradient أو RadialGradient أو فرشاة
SolidColor عادية.
يمكن استخدام الفُرش مع Modifier.background() أو TextStyle أو
DrawScope طلبات الرسم لتطبيق نمط الرسم على المحتوى
الذي يتم رسمه.
على سبيل المثال، يمكن تطبيق فرشاة تدرّج أفقي لرسم دائرة في DrawScope:
val brush = Brush.horizontalGradient(listOf(Color.Red, Color.Blue)) Canvas( modifier = Modifier.size(200.dp), onDraw = { drawCircle(brush) } )
فُرش التدرّج
تتوفّر العديد من فُرش التدرّج المضمّنة التي يمكن استخدامها لتحقيق تأثيرات تدرّج مختلفة. تتيح لك هذه الفُرش تحديد قائمة الألوان التي تريد إنشاء تدرّج منها.
في ما يلي قائمة بفُرش التدرّج المتاحة والناتج المقابل لها:
| نوع فرشاة التدرّج | الناتج |
|---|---|
Brush.horizontalGradient(colorList) |
|
Brush.linearGradient(colorList) |
|
Brush.verticalGradient(colorList) |
|
Brush.sweepGradient(colorList)
ملاحظة: للحصول على انتقال سلس بين الألوان، اضبط اللون الأخير على لون البداية. |
|
Brush.radialGradient(colorList) |
|
تغيير توزيع الألوان باستخدام colorStops
لتخصيص طريقة ظهور الألوان في التدرّج، يمكنك تعديل قيمة colorStops لكل لون. يجب تحديد colorStops ككسر بين 0 و1. ستؤدي القيم الأكبر من 1 إلى عدم عرض هذه الألوان كجزء من التدرّج.
يمكنك ضبط نقاط توقف الألوان لتضمّ مقادير مختلفة، مثل مقدار أقل أو أكبر من لون واحد:
val colorStops = arrayOf( 0.0f to Color.Yellow, 0.2f to Color.Red, 1f to Color.Blue ) Box( modifier = Modifier .requiredSize(200.dp) .background(Brush.horizontalGradient(colorStops = colorStops)) )
يتم توزيع الألوان عند الإزاحة المقدَّمة كما هو محدّد في زوج colorStop، مع مقدار أقل من اللون الأصفر مقارنةً بالأحمر والأزرق.
تكرار نمط باستخدام TileMode
يتوفّر لكل فرشاة تدرّج خيار ضبط TileMode عليها. قد لا تلاحظ TileMode إذا لم تضبط بداية ونهاية للتدرّج، لأنّه سيتم ملء المنطقة بأكملها تلقائيًا. لن يتم تكرار التدرّج باستخدام TileMode إلا إذا كان حجم المنطقة أكبر من حجم الفرشاة.
ستكرّر التعليمة البرمجية التالية نمط التدرّج 4 مرات، لأنّ endX تم ضبطه على 50.dp وتم ضبط الحجم على 200.dp:
val listColors = listOf(Color.Yellow, Color.Red, Color.Blue) val tileSize = with(LocalDensity.current) { 50.dp.toPx() } Box( modifier = Modifier .requiredSize(200.dp) .background( Brush.horizontalGradient( listColors, endX = tileSize, tileMode = TileMode.Repeated ) ) )
في ما يلي جدول يوضّح ما تفعله أوضاع التكرار المختلفة في مثال HorizontalGradient أعلاه:
| TileMode | الناتج |
|---|---|
TileMode.Repeated: يتم تكرار الحافة من اللون الأخير إلى الأول. |
|
TileMode.Mirror: يتم عكس الحافة من اللون الأخير إلى الأول. |
|
TileMode.Clamp: يتم تثبيت الحافة على اللون الأخير. بعد ذلك، يتم رسم اللون الأقرب لبقية المنطقة. |
|
TileMode.Decal: يتم العرض فقط حتى حجم الحدود. تستفيد TileMode.Decal من اللون الأسود الشفاف لأخذ عيّنات من المحتوى خارج الحدود الأصلية، بينما تأخذ TileMode.Clamp عيّنات من لون الحافة. |
|
تعمل TileMode بطريقة مشابهة لفُرش التدرّج الاتجاهية الأخرى، ولكن يختلف الاتجاه الذي يحدث فيه التكرار.
تغيير حجم الفرشاة
إذا كنت تعرف حجم المنطقة التي سيتم رسم الفرشاة فيها، يمكنك ضبط endX للمربّع كما رأينا أعلاه في قسم TileMode. إذا كنت تستخدم
DrawScope، يمكنك استخدام السمة size للحصول على حجم المنطقة.
إذا كنت لا تعرف حجم مساحة الرسم (على سبيل المثال، إذا تم تعيين
Brush إلى "نص")، يمكنك توسيع Shader واستخدام حجم مساحة الرسم في الدالة createShader.
في هذا المثال، قسِّم الحجم على 4 لتكرار النمط 4 مرات:
val listColors = listOf(Color.Yellow, Color.Red, Color.Blue) val customBrush = remember { object : ShaderBrush() { override fun createShader(size: Size): Shader { return LinearGradientShader( colors = listColors, from = Offset.Zero, to = Offset(size.width / 4f, 0f), tileMode = TileMode.Mirror ) } } } Box( modifier = Modifier .requiredSize(200.dp) .background(customBrush) )
يمكنك أيضًا تغيير حجم الفرشاة لأي تدرّج آخر، مثل التدرّجات الشعاعية. إذا لم تحدّد حجمًا ومركزًا، سيشغل التدرّج الحدود الكاملة لـ DrawScope، وسيكون مركز التدرّج الشعاعي تلقائيًا في منتصف حدود DrawScope. يؤدي ذلك إلى ظهور مركز التدرّج الشعاعي كمركز البُعد الأصغر (العرض أو الارتفاع):
Box( modifier = Modifier .fillMaxSize() .background( Brush.radialGradient( listOf(Color(0xFF2be4dc), Color(0xFF243484)) ) ) )
عند تغيير التدرّج الشعاعي لضبط حجم نصف القطر على الحد الأقصى، يمكنك ملاحظة أنّه ينتج تأثير تدرّج شعاعي أفضل:
val largeRadialGradient = object : ShaderBrush() { override fun createShader(size: Size): Shader { val biggerDimension = maxOf(size.height, size.width) return RadialGradientShader( colors = listOf(Color(0xFF2be4dc), Color(0xFF243484)), center = size.center, radius = biggerDimension / 2f, colorStops = listOf(0f, 0.95f) ) } } Box( modifier = Modifier .fillMaxSize() .background(largeRadialGradient) )
من الجدير بالذكر أنّ الحجم الفعلي الذي يتم تمريره إلى إنشاء أداة التظليل يتم تحديده من المكان الذي يتم استدعاؤه منه. تلقائيًا، ستعيد Brush تخصيص Shader داخليًا إذا كان الحجم مختلفًا عن آخر إنشاء لـ Brush، أو إذا تغيّر عنصر حالة مستخدَم في إنشاء أداة التظليل.
تنشئ التعليمة البرمجية التالية التظليل ثلاث مرات مختلفة بأحجام مختلفة، مع تغيُّر حجم مساحة الرسم:
val colorStops = arrayOf( 0.0f to Color.Yellow, 0.2f to Color.Red, 1f to Color.Blue ) val brush = Brush.horizontalGradient(colorStops = colorStops) Box( modifier = Modifier .requiredSize(200.dp) .drawBehind { drawRect(brush = brush) // will allocate a shader to occupy the 200 x 200 dp drawing area inset(10f) { /* Will allocate a shader to occupy the 180 x 180 dp drawing area as the inset scope reduces the drawing area by 10 pixels on the left, top, right, bottom sides */ drawRect(brush = brush) inset(5f) { /* will allocate a shader to occupy the 170 x 170 dp drawing area as the inset scope reduces the drawing area by 5 pixels on the left, top, right, bottom sides */ drawRect(brush = brush) } } } )
استخدام صورة كفرشاة
لاستخدام ImageBitmap كـ Brush، حمِّل الصورة كـ ImageBitmap،
وأنشئ فرشاة ImageShader:
val imageBrush = ShaderBrush(ImageShader(ImageBitmap.imageResource(id = R.drawable.dog))) // Use ImageShader Brush with background Box( modifier = Modifier .requiredSize(200.dp) .background(imageBrush) ) // Use ImageShader Brush with TextStyle Text( text = "Hello Android!", style = TextStyle( brush = imageBrush, fontWeight = FontWeight.ExtraBold, fontSize = 36.sp ) ) // Use ImageShader Brush with DrawScope#drawCircle() Canvas(onDraw = { drawCircle(imageBrush) }, modifier = Modifier.size(200.dp))
يتم تطبيق الفرشاة على أنواع مختلفة من الرسم: خلفية والنص وCanvas. ويظهر الناتج على النحو التالي:
لاحظ أنّه يتم الآن أيضًا عرض النص باستخدام ImageBitmap لرسم وحدات البكسل للنص.
مثال متقدّم: فرشاة مخصّصة
فرشاة RuntimeShader في AGSL
AGSL تقدّم مجموعة فرعية من إمكانات GLSL Shader. يمكن كتابة أدوات التظليل بلغة AGSL واستخدامها مع فرشاة في Compose.
لإنشاء فرشاة تظليل، عليك أولاً تحديد التظليل كسلسلة تظليل AGSL:
@Language("AGSL") val CUSTOM_SHADER = """ uniform float2 resolution; layout(color) uniform half4 color; layout(color) uniform half4 color2; half4 main(in float2 fragCoord) { float2 uv = fragCoord/resolution.xy; float mixValue = distance(uv, vec2(0, 1)); return mix(color, color2, mixValue); } """.trimIndent()
يأخذ التظليل أعلاه لونَين كمدخلات، ويحسب المسافة من أسفل يسار (vec2(0, 1)) مساحة الرسم، ويُجري عملية mix بين اللونَين استنادًا إلى المسافة. ينتج عن ذلك تأثير تدرّج.
بعد ذلك، أنشئ فرشاة أداة التظليل واضبط القيم الموحّدة لـ resolution (حجم مساحة الرسم) وcolor وcolor2 اللذين تريد استخدامهما كمدخلات لـ التدرّج المخصّص:
val Coral = Color(0xFFF3A397) val LightYellow = Color(0xFFF8EE94) @RequiresApi(Build.VERSION_CODES.TIRAMISU) @Composable @Preview fun ShaderBrushExample() { Box( modifier = Modifier .drawWithCache { val shader = RuntimeShader(CUSTOM_SHADER) val shaderBrush = ShaderBrush(shader) shader.setFloatUniform("resolution", size.width, size.height) onDrawBehind { shader.setColorUniform( "color", android.graphics.Color.valueOf( LightYellow.red, LightYellow.green, LightYellow .blue, LightYellow.alpha ) ) shader.setColorUniform( "color2", android.graphics.Color.valueOf( Coral.red, Coral.green, Coral.blue, Coral.alpha ) ) drawRect(shaderBrush) } } .fillMaxWidth() .height(200.dp) ) }
عند تشغيل هذه التعليمة البرمجية، يمكنك ملاحظة ما يلي معروضًا على الشاشة:
من الجدير بالذكر أنّه يمكنك إجراء الكثير من العمليات باستخدام التظليلات، وليس التدرّجات فقط، لأنّها تستند جميعها إلى العمليات الحسابية. لمزيد من المعلومات حول AGSL، اطّلِع على مستندات AGSL.
مراجع إضافية
لمزيد من الأمثلة على استخدام الفرشاة في Compose، اطّلِع على المراجع التالية:
- تحريك تلوين النص باستخدام الفرشاة في Compose 🖌️
- الرسومات والتنسيقات المخصّصة في Compose - مؤتمر Android Dev Summit لعام 2022
- تطبيق JetLagged كمثال - فرشاة RuntimeShader
مُقترَحة لك
- ملاحظة: يتم عرض نص الرابط عندما يكون JavaScript غير مفعّل
- معدِّلات الرسومات
- الرسومات في Compose
- تنسيق النص