الفرشاة: التدرجات والتظليل

يصف الرمز Brush في ميزة "الإنشاء" كيفية رسم عنصر على الشاشة: فهو يحدّد الألوان التي يتم رسمها في منطقة الرسم (أي دائرة أو مربّع أو مسار). هناك بعض الفرش المدمجة المفيدة للرسم، مثل 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)
    }
)

دائرة مرسومة باستخدام مخطط متدرّج أفقي
الشكل 1: دائرة مرسومة باستخدام مخطط انتقال أفقي

فرشاة التدرّج

هناك العديد من فرش التدرّج المضمّنة التي يمكن استخدامها لتحقيق تأثيرات التدرّج المختلفة. تتيح لك هذه الفرش تحديد قائمة الألوان التي تريد إنشاء تدرج منها.

قائمة بفرش التدرّج اللوني المتاحة والناتج المقابل لها:

نوع فرشاة التدرّج الإخراج
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))
)

يتم توزيع الألوان عند القيمة المعروضة في الحقل offset كما هو محدّد في colorStop pair، ويكون اللون الأصفر أقل من الأحمر والأزرق.

فرشاة تم ضبطها باستخدام نقاط توقف ألوان مختلفة
الشكل 2: فرشاة تم ضبطها باستخدام نقاط توقف ألوان مختلفة

تكرار نمط باستخدام 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)
)

حجم تظليل الصورة مقسومًا على 4
الشكل 3: حجم تظليل السطح مقسومًا على 4

يمكنك أيضًا تغيير حجم الفرشاة لأي تدرج آخر، مثل التدرجات الشعاعية. في حال عدم تحديد حجم ومركز، سيشغل التدرّج حدود DrawScope الكاملة، ويكون مركز التدرّج الشعاعي تلقائيًا في مركز حدود DrawScope. يؤدي ذلك إلى ظهور مركز التدرّج الشعاعي في منتصف السمة الأصغر (إما العرض أو الارتفاع):

Box(
    modifier = Modifier
        .fillMaxSize()
        .background(
            Brush.radialGradient(
                listOf(Color(0xFF2be4dc), Color(0xFF243484))
            )
        )
)

تم ضبط التدرّج الشعاعي بدون تغييرات في الحجم
الشكل 4: تم ضبط "التدرّج الشعاعي" بدون تغييرات في الحجم

عند تغيير التدرّج الشعاعي لضبط حجم نصف القطر على أقصى أبعاد، يمكنك ملاحظة أنّه ينتج تأثيرًا أفضل للتدرّج الشعاعي:

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)
)

نصف قطر أكبر في التدرّج الشعاعي، استنادًا إلى حجم المنطقة
الشكل 5: نطاق أكبر في التدرّج الشعاعي استنادًا إلى حجم المنطقة

تجدر الإشارة إلى أنّ الحجم الفعلي الذي يتم تمريره إلى عملية إنشاءshader يتم تحديده من مكان استدعائه. بشكلٍ تلقائي، سيعيد Brush تخصيص Shader داخليًا إذا كان الحجم مختلفًا عن عملية إنشاء Brush الأخيرة، أو إذا تغيّر كائن الحالة المستخدَم في إنشاءshader.

تنشئ التعليمة البرمجية التالية مخطّط الألوان ثلاث مرات مختلفة بأحجام مختلفة، وذلك عندما يتغيّر حجم مساحة الرسم:

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))

يتم تطبيق الفرشاة على بعض أنواع الرسم المختلفة: الخلفية والنص واللوحة. يعرض هذا الإجراء ما يلي:

استخدام فرشاة ImageShader بطرق مختلفة
الشكل 6: استخدام فرشاة ImageShader لرسم خلفية ونص ودائرة

لاحظ أنّه يتم الآن أيضًا عرض النص باستخدام ImageBitmap لرسم وحدات البكسل للنص.

مثال متقدّم: فرشاة مخصّصة

فرشاة AGSL RuntimeShader

يوفّر AGSL مجموعة فرعية من إمكانات Shader في GLSL. يمكن كتابة مواد التشطيب باستخدام AGSL واستخدامها مع فرشاة في أداة "الإنشاء".

لإنشاء فرشاة Shader، حدِّد أولاً Shader كسلسلة Shader في 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 مخصّص في أداة Compose
الشكل 7: برنامج تشويش مخصّص لـ AGSL يتم تشغيله في أداة "الإنشاء"

تجدر الإشارة إلى أنّه يمكنك إجراء الكثير من الإجراءات باستخدام مخطّطات التظليل أكثر من مجرد التدرّجات، لأنّها جميعها عمليات حسابية مستندة إلى الرياضيات. لمزيد من المعلومات حول AGSL، يُرجى الاطّلاع على مستندات AGSL.

مصادر إضافية

لمزيد من الأمثلة على استخدام أداة "الأداة المخصّصة" في ميزة "الإنشاء"، يمكنك الاطّلاع على المراجع التالية: