אמנם מערכת העיצוב של Material היא המומלצת שלנו, ו-Jetpack Compose כולל הטמעה של Material, אבל אתם לא חייבים להשתמש בה. חומר הלימוד מבוסס באופן מלא על ממשקי API ציבוריים, כך שאפשר ליצור מערכת עיצוב משלכם באותו אופן.
יש כמה גישות אפשריות:
- הרחבת
MaterialTheme
בעזרת ערכים נוספים של עיצוב - החלפת מערכת אחת או יותר של Material –
Colors
,Typography
אוShapes
– בהטמעות בהתאמה אישית, תוך שמירה על המערכות האחרות - הטמעה של מערכת עיצוב בהתאמה אישית מלאה כדי להחליף את
MaterialTheme
אפשר גם להמשיך להשתמש ברכיבי Material עם מערכת עיצוב בהתאמה אישית. אתם יכולים לעשות את זה, אבל יש כמה דברים שכדאי לזכור כדי להתאים לגישה שלכם.
מידע נוסף על המבנים ברמה נמוכה יותר ועל ממשקי ה-API שבהם MaterialTheme
ומערכות עיצוב בהתאמה אישית משתמשים זמין במדריך Anatomy of a theme in Compose.
עיצוב חומר מתרחב
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)
כך אפשר לשמור על עקביות עם ממשקי ה-API לדיווח על שימוש ב-MaterialTheme
. דוגמה לכך היא surfaceColorAtElevation
, שמוגדר על ידי Compose עצמו ומחליט איזה צבע להשתמש בו לפי הגובה.
גישה אחרת היא להגדיר עיצוב מורחב ש"עוטף" את MaterialTheme
ואת הערכים שלו.
נניח שאתם רוצים להוסיף עוד שני צבעים – caution
ו-onCaution
, צבע צהוב המשמש לפעולות מסוכנות למחצה, תוך שמירה על צבעי החומר הקיימים:
@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 }
זה דומה לממשקי API לדיווח על שימוש ב-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, כדאי להחליף מערכת אחת או יותר – 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 = { /* ... */ }) { /* ... */ } } }
הטמעת מערכת עיצוב בהתאמה אישית מלאה
כדאי להחליף את מערכת העיצוב של Material Theming במערכת עיצוב מותאמת אישית לחלוטין.
נניח ש-MaterialTheme
מספק את המערכות הבאות:
Colors
,Typography
ו-Shapes
: מערכות של התאמת עיצוב של Google Material DesignTextSelectionColors
: הצבעים שבהם נעשה שימוש לבחירת טקסט על ידיText
ו-TextField
Ripple
ו-RippleTheme
: הטמעת חומר שלIndication
אם אתם רוצים להמשיך להשתמש ברכיבי Material, תצטרכו להחליף חלק מהמערכות האלה בעיצוב או בעיצובים המותאמים אישית שלכם, או לטפל במערכות ברכיבים שלכם כדי למנוע התנהגות לא רצויה.
עם זאת, מערכות עיצוב לא מוגבלות לקונספט של Material. אפשר לשנות מערכות קיימות ולהציג מערכות חדשות לגמרי – עם סוגים ודפים חדשים – כדי ליצור תאימות בין קונספטים אחרים לבין נושאים.
בקוד הבא, אנחנו יוצרים מודל של מערכת צבעים מותאמת אישית שכוללת מעברים (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 כפי שהם יגרום לערכים לא רצויים של צבע, סוג וצורה, כמו גם פעולה של סימנים.
אם רוצים להשתמש בערכים מותאמים אישית ברכיבים, צריך לתחום אותם בפונקציות קומפוזביליות משלכם, להגדיר באופן ישיר את הערכים למערכת הרלוונטית ולחשוף אחרים כפרמטרים לתוכן הקומפוזבילי המכיל.
מומלץ לגשת לערכים שהגדרתם מהעיצוב המותאם אישית.
לחלופין, אם העיצוב לא מספק 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 ב-Compose
- העברה מחומר 2 לחומר 3 ב-Compose
- מבנה של נושא ב-Compose