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

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

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

مصادر إضافية

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