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

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

  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.

دمج كائنات نمط متعددة

يمكنك إنشاء كائنات نمط متعددة وتمريرها إلى مَعلمة النمط الخاصة بالدالة المركّبة.

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