أساسيات الأنماط

هناك ثلاث طرق يمكنك من خلالها استخدام "الأنماط" في تطبيقك:

  1. يمكنك استخدامها مباشرةً في المكوّنات الحالية التي تعرض المَعلمة Style.
  2. تطبيق نمط يتضمّن Modifier.styleable على عناصر قابلة للإنشاء في التصميم لا تقبل المَعلمة Style
  3. في نظام التصميم المخصّص الخاص بك، استخدِم Modifier.styleable{} واعرض مَعلمة نمط على المكوّنات الخاصة بك.

السمات المتاحة في "الأنماط"

تتضمّن الأنماط العديد من الخصائص نفسها التي تتضمّنها المعدِّلات، ولكن لا يمكن تكرار كل ما هو معدِّل باستخدام نمط. ستظل بحاجة إلى أدوات تعديل لبعض السلوكيات، مثل التفاعلات أو الرسومات المخصّصة أو ترتيب الخصائص.

التجميع الخصائص مكتسبة من الوالدَين
التنسيق وتحديد الحجم
مساحة متروكة contentPadding (داخلي) وexternalPadding (خارجي) تتوفّر بأشكال مختلفة، مثل الاتجاهية والأفقية والعمودية والجوانب الأربعة. لا
الأبعاد fillWidth/Height/Size() وwidth وheight وsize (يتيح استخدام كسور Dp أو DpSize أو Float) لا
إقناع العملاء بالتبديل left/top/right/bottom إزاحة لا
المظهر المرئي
ممتلئ background وforeground (متوافق مع Color أو Brush) لا
الحدود borderWidth وborderColor وborderBrush لا
الشكل shape لا، ولكن يتم استخدامه مع مواقع إلكترونية أخرى. تستخدم clip وborder هذا الشكل المحدّد.
تظليل dropShadow، innerShadow لا
عمليات التحويل
الحركة المكانية لطبقة الرسومات translationX، translationY، scaleX/Y، rotationX/Y/Z لا
التحكّم alpha وzIndex (ترتيب التراكم) وtransformOrigin (نقطة الارتكاز) لا
أسلوب الخط
التصميم textStyle وfontSize وfontWeight وfontStyle وfontFamily نعم
الألوان contentColor وcontentBrush يُستخدم هذا الخيار أيضًا لتحديد نمط الرموز. نعم
فقرة lineHeight وletterSpacing وtextAlign وtextDirection وlineBreak وhyphens نعم
زخرفة textDecoration وtextIndent وbaselineShift نعم

استخدام "الأنماط" مباشرةً على المكوّنات باستخدام مَعلمات "النمط"

تتيح لك المكوّنات التي تعرض المَعلمة Style ضبط نمطها:

BaseButton(
    onClick = { },
    style = { }
) {
    BaseText("Click me")
}

يمكنك ضمن دالة lambda الخاصة بالتصميم ضبط سمات مختلفة، مثل externalPadding أو background:

BaseButton(
    onClick = { },
    style = { background(Color.Blue) }
) {
    BaseText("Click me")
}

للاطّلاع على القائمة الكاملة بالسمات المتوافقة، يُرجى الرجوع إلى السمات المتاحة في "الأنماط".

تطبيق الأنماط باستخدام أدوات التعديل على المكوّنات التي لا تتضمّن مَعلمة حالية

بالنسبة إلى المكوّنات التي لا تتضمّن مَعلمة نمط مضمّنة، يمكنك تطبيق الأنماط باستخدام المعدِّل styleable. ويكون هذا النهج مفيدًا أيضًا عند تطوير مكوّنات مخصّصة.

Row(
    modifier = Modifier.styleable { }
) {
    BaseText("Content")
}

على غرار المَعلمة style، يمكنك تضمين سمات مثل background أو padding داخل lambda.

Row(
    modifier = Modifier.styleable {
        background(Color.Blue)
    }
) {
    BaseText("Content")
}

تكون معدِّلات Modifier.styleable المتسلسلة المتعدّدة مضافة إلى الخصائص غير الموروثة في العنصر القابل للإنشاء الذي تم تطبيقه، وتتصرّف بشكل مشابه للمعدِّلات المتعدّدة التي تحدّد الخصائص نفسها. بالنسبة إلى الخصائص الموروثة، يتم إلغاء هذه الخصائص، ويضبط المعدِّل الأخير styleable في السلسلة القيم.

عند استخدام Modifier.styleable، قد تحتاج أيضًا إلى إنشاء StyleState وتوفيره لاستخدامه مع المعدِّل من أجل تطبيق أنماط مستندة إلى الحالة. لمزيد من التفاصيل، اطّلِع على الحالات والحركات باستخدام الأنماط.

تحديد نمط مستقل

يمكنك تحديد نمط مستقل لأغراض إعادة الاستخدام:

val style = Style { background(Color.Blue) }

يمكنك بعد ذلك تمرير هذا النمط المحدّد إلى مَعلمة النمط في عنصر قابل للإنشاء أو باستخدام Modifier.styleable. عند استخدام Modifier.styleable، عليك أيضًا إنشاء عنصر StyleState. يتم تناول StyleState بالتفصيل في مستندات الحالة والرسوم المتحركة باستخدام "الأنماط".

يوضّح المثال التالي كيف يمكنك تطبيق نمط إما مباشرةً من خلال المَعلمات المضمّنة في أحد المكوّنات، أو من خلال Modifier.styleable:

val style = Style { background(Color.Blue) }

// built in parameter
BaseButton(onClick = { }, style = style) {
    BaseText("Button")
}

// modifier styleable
val styleState = remember { MutableStyleState(null) }
Column(
    Modifier.styleable(styleState, style)
) {
    BaseText("Column content")
}

يمكنك أيضًا تمرير هذا النمط إلى عدة مكوّنات:

val style = Style { background(Color.Blue) }

// built in parameter
BaseButton(onClick = { }, style = style) {
    BaseText("Button")
}
BaseText("Different text that uses the same style parameter", style = style)

// modifier styleable
val columnStyleState = remember { MutableStyleState(null) }
Column(
    Modifier.styleable(columnStyleState, style)
) {
    BaseText("Column")
}
val rowStyleState = remember { MutableStyleState(null) }
Row(
    Modifier.styleable(rowStyleState, style)
) {
    BaseText("Row")
}

إضافة عدة خصائص نمط

يمكنك إضافة خصائص "النمط" المتعددة من خلال ضبط خصائص مختلفة في كل سطر:

BaseButton(
    onClick = { },
    style = {
        background(Color.Blue)
        contentPaddingStart(16.dp)
    }
) {
    BaseText("Button")
}

لا يمكن إضافة خصائص في "الأنماط"، على عكس التنسيق المستند إلى المعدِّلات. تستخدم الأنماط القيمة الأخيرة التي تم ضبطها في قائمة الخصائص ضمن حزمة نمط واحدة. في المثال التالي، تم ضبط الخلفية مرتين، وبالتالي فإنّ TealColor هي الخلفية التي تم تطبيقها. بالنسبة إلى المساحة المتروكة، تلغي contentPaddingTop المساحة المتروكة العلوية التي تم ضبطها بواسطة contentPadding ولا تجمع القيم.

BaseButton(
    style = {
        background(Color.Red)
        // Background of Red is now overridden with TealColor instead
        background(TealColor)
        // All directions of padding are set to 64.dp (top, start, end, bottom)
        contentPadding(64.dp)
        // Top padding is now set to 16.dp, all other paddings remain at 64.dp
        contentPaddingTop(16.dp)
    },
    onClick = {
        //
    }
) {
    BaseText("Click me!")
}

زر تم ضبط لونَي خلفيته، وتمت إضافة قيمتَي contentPadding
لتجاوز القيم التلقائية
الشكل 1. زر تم ضبط لونَي خلفية له وcontentPaddingعمليتَي إلغاء

دمج عناصر أنماط متعدّدة

يمكنك إنشاء عناصر Style متعددة وتمريرها إلى مَعلمة النمط في العنصر القابل للإنشاء.

val style1 = Style { background(TealColor) }
val style2 = Style { contentPaddingTop(16.dp) }

BaseButton(
    style = style1 then style2,
    onClick = {

    },
) {
    BaseText("Click me!")
}

زرّ بلون خلفية وتم ضبط contentPaddingTop
الشكل 2. زر بلون خلفية تم ضبطه على contentPaddingTop

عندما تحدّد عدة "أنماط" السمة نفسها، يتم اختيار السمة التي تم ضبطها آخر مرة. بما أنّ الخصائص ليست إضافية في "الأنماط"، فإنّ آخر قيمة تم تمريرها للحشو الداخلي تلغي القيمة contentPaddingHorizontal التي تم ضبطها بواسطة contentPadding الأولية. بالإضافة إلى ذلك، يلغي لون الخلفية الأخير لون الخلفية الذي تم ضبطه بواسطة النمط الأولي الذي تم تمريره.

val style1 = Style {
    background(Color.Red)
    contentPadding(32.dp)
}

val style2 = Style {
    contentPaddingHorizontal(8.dp)
    background(Color.LightGray)
}

BaseButton(
    style = style1 then style2,
    onClick = {

    },
) {
    BaseText("Click me!")
}

في هذه الحالة، يكون التنسيق المطبَّق بخلفية رمادية فاتحة ومساحة متروكة 32.dp، باستثناء المساحة المتروكة على اليمين واليسار التي تبلغ قيمتها 8.dp.

زر يتضمّن contentPadding تم تجاهله بسبب أنماط مختلفة
الشكل 3. زر يتضمّن contentPadding تم استبداله بأنماط مختلفة.

اكتساب الأنماط

تنتقل بعض خصائص الأنماط، مثل contentColor والخصائص ذات الصلة بنمط النص، إلى العناصر القابلة للإنشاء الفرعية. يؤدي ضبط نمط على عنصر فرعي قابل للإنشاء إلى إلغاء نمط العنصر الرئيسي الموروث لهذا العنصر الفرعي المحدّد.

تطبيق النمط باستخدام المَعلمات Style وstyleable وdirect
الشكل 4. نشر الأنماط باستخدام Style وstyleable والمعلَمات المباشرة
درجة الأهمية الطريقة التأثير
1 (الأعلى) المَعلمات المباشرة في عنصر قابل للإنشاء تتجاوز كل شيء، مثلاً Text(color = Color.Red)
2 مَعلمة النمط عمليات إلغاء النمط المحلي Text(style = Style { contentColor(Color.Red)}
3 سلسلة أدوات التعديل Modifier.styleable{ contentColor(Color.Red) على المكوّن نفسه.
‫4 (الأدنى) الأنماط الرئيسية بالنسبة إلى الخصائص التي يمكن اكتسابها (أسلوب الخط/اللون) من العنصر الرئيسي

النمط الرئيسي

يمكنك ضبط خصائص النص (مثل contentColor) من العنصر القابل للإنشاء الرئيسي، ويتم نقلها إلى جميع العناصر القابلة للإنشاء الفرعية Text.

val styleState = remember { MutableStyleState(null) }
Column(
    modifier = Modifier.styleable(styleState) {
        background(Color.LightGray)
        val blue = Color(0xFF4285F4)
        val purple = Color(0xFFA250EA)
        val colors = listOf(blue, purple)
        contentBrush(Brush.linearGradient(colors))
    },
) {
    BaseText("Children inherit", style = { width(60.dp) })
    BaseText("certain properties")
    BaseText("from their parents")
}

توريث خصائص العناصر القابلة للإنشاء الفرعية
الشكل 5. اكتساب المواقع الإلكترونية الفرعية للخصائص

إلغاء المواقع الإلكترونية الفرعية لسمات الموقع الرئيسي

يمكنك أيضًا ضبط الأنماط على عنصر Text قابل للإنشاء محدّد. إذا تم ضبط نمط على العنصر القابل للإنشاء الرئيسي، سيؤدي ضبط نمط على العنصر القابل للإنشاء الفرعي إلى إلغاء نمط العنصر القابل للإنشاء الرئيسي.

val styleState = remember { MutableStyleState(null) }
Column(
    modifier = Modifier.styleable(styleState) {
        background(Color.LightGray)
        val blue = Color(0xFF4285F4)
        val purple = Color(0xFFA250EA)
        val colors = listOf(blue, purple)
        contentBrush(Brush.linearGradient(colors))
    },
) {
    BaseText("Children can ", style = {
        contentBrush(Brush.linearGradient(listOf(Color.Red, Color.Blue)))
    })
    BaseText("override properties")
    BaseText("set by their parents")
}

تتجاوز العناصر القابلة للإنشاء الفرعية خصائص العناصر القابلة للإنشاء الرئيسية
الشكل 6. تتجاوز العناصر القابلة للإنشاء الفرعية خصائص العناصر الرئيسية.

تنفيذ خصائص "النمط" المخصّصة

يمكنك إنشاء سمات مخصّصة يتم ربطها بتعريفات الأنماط الحالية باستخدام دوال الإضافة على StyleScope، كما هو موضّح في المثال التالي:

fun StyleScope.outlinedBackground(color: Color) {
    border(1.dp, color)
    background(color)
}

طبِّق هذه السمة الجديدة ضمن تعريف "النمط":

val customExtensionStyle = Style {
    outlinedBackground(Color.Blue)
}

لا يمكن إنشاء سمات جديدة قابلة للتصميم. إذا كانت حالة الاستخدام تتطلّب هذا النوع من الدعم، يُرجى إرسال طلب ميزة.

قراءة قيم CompositionLocal

من الأنماط الشائعة تخزين رموز نظام التصميم ضمن CompositionLocal، للوصول إلى المتغيرات بدون الحاجة إلى تمريرها كمعلَمات. يمكن أن تصل الأنماط إلى CompositionLocal لاسترداد القيم على مستوى النظام ضمن نمط:

val buttonStyle = Style {
    contentPadding(12.dp)
    shape(RoundedCornerShape(50))
    background(Brush.verticalGradient(LocalCustomColors.currentValue.background))
}