على الرغم من أنّ Material هو نظام التصميم المُقترَح لدينا وأنّ Jetpack Compose يُرسِل تكاملًا مع Material، لا يُفرَض عليك استخدامه. تم إنشاء Material كليًا استنادًا إلى واجهات برمجة التطبيقات العامة، لذا من الممكن إنشاء نظام التصميم الخاص بك بالطريقة نفسها.
هناك عدة طرق يمكنك اتّخاذها:
- توسيع
MaterialTheme
باستخدام قيم إضافية لتنسيق التطبيقات - استبدال نظام Material واحد أو أكثر، مثل
Colors
أوTypography
أوShapes
، بعمليات تنفيذ مخصّصة مع الاحتفاظ بالأنظمة الأخرى - تنفيذ نظام تصميم مخصّص بالكامل بهدف
استبدال
MaterialTheme
قد تحتاج أيضًا إلى مواصلة استخدام مكونات Material Design مع نظام تصميم مخصّص. من الممكن إجراء ذلك، ولكن هناك بعض النقاط التي يجب أخذها بعين الاعتبار لتتلاءم مع المنهج الذي اتّبعته.
لمزيد من المعلومات حول العناصر وواجهات برمجة التطبيقات ذات المستوى الأدنى التي يستخدمها MaterialTheme
وأنظمة التصميم المخصّصة، اطّلِع على دليل بنية المظهر في Compose.
توسيع نطاق "مظهر المواد"
تستند ميزة "إنشاء تصميمات Material" بشكل وثيق إلى مظاهر Material لجعل اتّباع إرشادات Material أمرًا بسيطًا وآمنًا من حيث النوع. ومع ذلك، يمكن توسيع مجموعات الألوان وأسلوب الخط والأشكال باستخدام قيم إضافية.
إنّ أبسط طريقة هي إضافة سمات الإضافة:
// Use with MaterialTheme.colorScheme.snackbarAction val ColorScheme.snackbarAction: Color @Composable get() = if (isSystemInDarkTheme()) Red300 else Red700 // Use with MaterialTheme.typography.textFieldInput val Typography.textFieldInput: TextStyle get() = TextStyle(/* ... */) // Use with MaterialTheme.shapes.card val Shapes.card: Shape get() = RoundedCornerShape(size = 20.dp)
ويضمن ذلك اتّساق واجهات برمجة التطبيقات الخاصة باستخدام MaterialTheme
. ومن الأمثلة على ذلك
المحدّد من خلال Compose نفسه هو
surfaceColorAtElevation
،
الذي يحدّد لون السطح الذي يجب استخدامه استنادًا إلى الارتفاع.
هناك نهج آخر وهو تحديد موضوع موسّع "يلفّ" MaterialTheme
وقيَمه.
لنفترض أنّك تريد إضافة لونَين إضافيَين، caution
وonCaution
، وهو
لون أصفر يُستخدَم للإجراءات شبه الخطيرة، مع الاحتفاظ
بألوان Material الحالية:
@Immutable data class ExtendedColors( val caution: Color, val onCaution: Color ) val LocalExtendedColors = staticCompositionLocalOf { ExtendedColors( caution = Color.Unspecified, onCaution = Color.Unspecified ) } @Composable fun ExtendedTheme( /* ... */ content: @Composable () -> Unit ) { val extendedColors = ExtendedColors( caution = Color(0xFFFFCC02), onCaution = Color(0xFF2C2D30) ) CompositionLocalProvider(LocalExtendedColors provides extendedColors) { MaterialTheme( /* colors = ..., typography = ..., shapes = ... */ content = content ) } } // Use with eg. ExtendedTheme.colors.caution object ExtendedTheme { val colors: ExtendedColors @Composable get() = LocalExtendedColors.current }
وهذا مشابه لواجهات برمجة التطبيقات الخاصة باستخدام MaterialTheme
. وهو يتيح أيضًا استخدام مظاهر متعددة،
إذ يمكنك إدراج ExtendedTheme
بالطريقة نفسها التي يتم بها إدراج MaterialTheme
.
استخدام مكونات Material
عند توسيع نطاق "مظاهر Material"، يتم الاحتفاظ بقيم MaterialTheme
الحالية
ولا تزال مكوّنات Material تتضمّن قيمًا تلقائية معقولة.
إذا كنت تريد استخدام قيم موسّعة في المكوّنات، لفها في وظائفك القابلة للتجميع، مع ضبط القيم التي تريد تغييرها مباشرةً، وعرض القيم الأخرى كمَعلمات للعنصر القابل للتجميع الذي يحتوي عليها:
@Composable fun ExtendedButton( onClick: () -> Unit, modifier: Modifier = Modifier, content: @Composable RowScope.() -> Unit ) { Button( colors = ButtonDefaults.buttonColors( containerColor = ExtendedTheme.colors.caution, contentColor = ExtendedTheme.colors.onCaution /* Other colors use values from MaterialTheme */ ), onClick = onClick, modifier = modifier, content = content ) }
بعد ذلك، يمكنك استبدال استخدامات Button
بـ ExtendedButton
عند الاقتضاء.
@Composable fun ExtendedApp() { ExtendedTheme { /*...*/ ExtendedButton(onClick = { /* ... */ }) { /* ... */ } } }
استبدال الأنظمة الفرعية لتصميم Material
بدلاً من توسيع نطاق Material Theming، قد تحتاج إلى استبدال أحد الأنظمة أو أكثر، مثل Colors
أو Typography
أو Shapes
، بتنفيذ مخصّص، مع الاحتفاظ بالأنظمة الأخرى.
لنفترض أنّك تريد استبدال نظامَي النوع والشكل مع الاحتفاظ بنظام الألوان:
@Immutable data class ReplacementTypography( val body: TextStyle, val title: TextStyle ) @Immutable data class ReplacementShapes( val component: Shape, val surface: Shape ) val LocalReplacementTypography = staticCompositionLocalOf { ReplacementTypography( body = TextStyle.Default, title = TextStyle.Default ) } val LocalReplacementShapes = staticCompositionLocalOf { ReplacementShapes( component = RoundedCornerShape(ZeroCornerSize), surface = RoundedCornerShape(ZeroCornerSize) ) } @Composable fun ReplacementTheme( /* ... */ content: @Composable () -> Unit ) { val replacementTypography = ReplacementTypography( body = TextStyle(fontSize = 16.sp), title = TextStyle(fontSize = 32.sp) ) val replacementShapes = ReplacementShapes( component = RoundedCornerShape(percent = 50), surface = RoundedCornerShape(size = 40.dp) ) CompositionLocalProvider( LocalReplacementTypography provides replacementTypography, LocalReplacementShapes provides replacementShapes ) { MaterialTheme( /* colors = ... */ content = content ) } } // Use with eg. ReplacementTheme.typography.body object ReplacementTheme { val typography: ReplacementTypography @Composable get() = LocalReplacementTypography.current val shapes: ReplacementShapes @Composable get() = LocalReplacementShapes.current }
استخدام مكونات Material
عند استبدال نظام واحد أو أكثر من MaterialTheme
، قد يؤدي استخدام مكونات Material
كما هي إلى ظهور قيم غير مرغوب فيها للون أو النوع أو الشكل في Material.
إذا كنت تريد استخدام قيم بديلة في المكوّنات، عليك لفّها في وظائفك القابلة للتجميع، وضبط القيم للنظام ذي الصلة مباشرةً، وعرض القيم الأخرى كمَعلمات للعنصر القابل للتجميع الذي يحتوي عليها.
@Composable fun ReplacementButton( onClick: () -> Unit, modifier: Modifier = Modifier, content: @Composable RowScope.() -> Unit ) { Button( shape = ReplacementTheme.shapes.component, onClick = onClick, modifier = modifier, content = { ProvideTextStyle( value = ReplacementTheme.typography.body ) { content() } } ) }
بعد ذلك، يمكنك استبدال استخدامات Button
بـ ReplacementButton
عند الاقتضاء.
@Composable fun ReplacementApp() { ReplacementTheme { /*...*/ ReplacementButton(onClick = { /* ... */ }) { /* ... */ } } }
تنفيذ نظام تصميم مخصّص بالكامل
قد تحتاج إلى استبدال ميزة "تخصيص التصميم المتعدد الأبعاد" بنظام تصميم مخصّص بالكامل.
لنفترض أنّ MaterialTheme
يوفّر الأنظمة التالية:
-
Colors
وTypography
وShapes
: أنظمة تخصيص التصميم المتعدد الأبعاد TextSelectionColors
: الألوان المستخدَمة لاختيار النص من خلالText
وTextField
-
Ripple
وRippleTheme
: تنفيذIndication
في مادة
إذا كنت تريد مواصلة استخدام مكونات Material، عليك استبدال بعض هذه الأنظمة في المظهر المخصّص أو المظاهر المخصّصة، أو التعامل مع الأنظمة في المكوّنات لتجنّب السلوك غير المرغوب فيه.
ومع ذلك، لا تقتصر أنظمة التصميم على المفاهيم التي يعتمد عليها Material Design. يمكنك تعديل الأنظمة الحالية وتقديم أنظمة جديدة تمامًا، باستخدام فئات وأنواع جديدة، لجعل المفاهيم الأخرى متوافقة مع المظاهر.
في الرمز البرمجي التالي، نضع نموذجًا لنظام ألوان مخصّص يتضمّن تدرّجات List<Color>
، ونضيف نظام أنواع، ونقدّم نظامًا جديدًا للارتفاع،
ونستبعد الأنظمة الأخرى التي يوفّرها MaterialTheme
:
@Immutable data class CustomColors( val content: Color, val component: Color, val background: List<Color> ) @Immutable data class CustomTypography( val body: TextStyle, val title: TextStyle ) @Immutable data class CustomElevation( val default: Dp, val pressed: Dp ) val LocalCustomColors = staticCompositionLocalOf { CustomColors( content = Color.Unspecified, component = Color.Unspecified, background = emptyList() ) } val LocalCustomTypography = staticCompositionLocalOf { CustomTypography( body = TextStyle.Default, title = TextStyle.Default ) } val LocalCustomElevation = staticCompositionLocalOf { CustomElevation( default = Dp.Unspecified, pressed = Dp.Unspecified ) } @Composable fun CustomTheme( /* ... */ content: @Composable () -> Unit ) { val customColors = CustomColors( content = Color(0xFFDD0D3C), component = Color(0xFFC20029), background = listOf(Color.White, Color(0xFFF8BBD0)) ) val customTypography = CustomTypography( body = TextStyle(fontSize = 16.sp), title = TextStyle(fontSize = 32.sp) ) val customElevation = CustomElevation( default = 4.dp, pressed = 8.dp ) CompositionLocalProvider( LocalCustomColors provides customColors, LocalCustomTypography provides customTypography, LocalCustomElevation provides customElevation, content = content ) } // Use with eg. CustomTheme.elevation.small object CustomTheme { val colors: CustomColors @Composable get() = LocalCustomColors.current val typography: CustomTypography @Composable get() = LocalCustomTypography.current val elevation: CustomElevation @Composable get() = LocalCustomElevation.current }
استخدام مكونات Material
في حال عدم توفّر MaterialTheme
، سيؤدي استخدام مكوّنات Material كما هي إلى
ظهور قيم وسلوك غير مرغوب فيه للون ونوع وشكل Material.
إذا كنت تريد استخدام قيم مخصّصة في المكوّنات، يمكنك لفّها في وظائفك المكوّنة الخاصة، وضبط القيم للنظام ذي الصلة مباشرةً، وعرض قيم أخرى كمَعلمات للعنصر المكوّن الذي يحتوي عليها.
ننصحك بالوصول إلى القيم التي تحدّدها من تصميمك المخصّص.
بدلاً من ذلك، إذا كان المظهر لا يقدّم Color
أو TextStyle
أو Shape
أو
أنظمة أخرى، يمكنك تضمينها في الرمز البرمجي.
@Composable fun CustomButton( onClick: () -> Unit, modifier: Modifier = Modifier, content: @Composable RowScope.() -> Unit ) { Button( colors = ButtonDefaults.buttonColors( containerColor = CustomTheme.colors.component, contentColor = CustomTheme.colors.content, disabledContainerColor = CustomTheme.colors.content .copy(alpha = 0.12f) .compositeOver(CustomTheme.colors.component), disabledContentColor = CustomTheme.colors.content .copy(alpha = 0.38f) ), shape = ButtonShape, elevation = ButtonDefaults.elevatedButtonElevation( defaultElevation = CustomTheme.elevation.default, pressedElevation = CustomTheme.elevation.pressed /* disabledElevation = 0.dp */ ), onClick = onClick, modifier = modifier, content = { ProvideTextStyle( value = CustomTheme.typography.body ) { content() } } ) } val ButtonShape = RoundedCornerShape(percent = 50)
إذا كنت قد أدخلت أنواعًا جديدة من الفئات، مثل List<Color>
لتمثيل
التدرّجات، قد يكون من الأفضل تنفيذ المكوّنات من البداية بدلاً من
تغليفها. على سبيل المثال، اطّلِع على
JetsnackButton
من نموذج Jetsnack.
أفلام مُقترَحة لك
- ملاحظة: يتم عرض نص الرابط عندما تكون لغة JavaScript غير مفعّلة.
- Material Design 3 في ميزة "الكتابة"
- نقل البيانات من "المادة 2" إلى "المادة 3" في ميزة "الإنشاء"
- تحليل بنية المظهر في ميزة "الإنشاء"