Material Design 2 בכתיבה

ב-Jetpack Compose יש הטמעה של עיצוב חדשני תלת-ממדי, מערכת עיצוב מקיפה ליצירת ממשקים דיגיטליים. רכיבי Material Design (לחצנים, כרטיסים, מתגים וכו') מבוססים על עיצוב לפי נושאים ב-Material, שהיא דרך שיטתית להתאמה אישית של Material Design כך שישקף טוב יותר את המותג של המוצר. חומר לימוד העיצוב כולל את מאפייני הצבע, טיפוגרפיה והצורה. כאשר אתם מתאימים אישית את ההגדרות האלה שלך, השינויים שלך ישתקפו באופן אוטומטי ברכיבים שבהם אתה משתמש כדי לפתח את האפליקציה.

ב-Jetpack Compose, המושגים האלה מיושמים באמצעות ה-composable‏ MaterialTheme:

MaterialTheme(
    colors = // ...
    typography = // ...
    shapes = // ...
) {
    // app content
}

מגדירים את הפרמטרים ששולחים אל MaterialTheme כדי להגדיר את העיצוב של האפליקציה.

שתי צילומי מסך מנוגדים. השיטה הראשונה משתמשת בעיצוב ברירת המחדל של MaterialTheme,
בצילום המסך השני נעשה שימוש בעיצוב שונה.

איור 1. בצילום המסך הראשון רואים אפליקציה שלא מוגדרת MaterialTheme, ולכן נעשה בו שימוש בעיצוב ברירת המחדל. בצילום המסך השני רואים אפליקציה שמעבירה פרמטרים אל MaterialTheme כדי להתאים אישית את הסגנון.

צבע

הצבעים מוגדרים ב-Compose באמצעות הכיתה Color, שהיא כיתה פשוטה לאחסון נתונים.

val Red = Color(0xffff0000)
val Blue = Color(red = 0f, green = 0f, blue = 1f)

אתם יכולים לארגן אותם איך שתרצו (כקבועים ברמה העליונה, במסגרת סינגלטון או מוגדר בתוך השורה), אנחנו ממליצים מאוד לציין צבעים את העיצוב ומאחזר את הצבעים. הגישה הזו מאפשרת לתמוך בקלות בעיצוב כהה ובעיצובים בתצוגת עץ.

דוגמה ללוח הצבעים של העיצוב

איור 2. מערכת הצבעים של Material.

Compose מספק את הכיתה Colors כדי ליצור מודל של מערכת הצבעים של Material. ב-Colors יש פונקציות build ליצירת קבוצות של צבעים בהירים או כהים:

private val Yellow200 = Color(0xffffeb46)
private val Blue200 = Color(0xff91a4fc)
// ...

private val DarkColors = darkColors(
    primary = Yellow200,
    secondary = Blue200,
    // ...
)
private val LightColors = lightColors(
    primary = Yellow500,
    primaryVariant = Yellow400,
    secondary = Blue700,
    // ...
)

אחרי שמגדירים את Colors, אפשר להעביר אותם ל-MaterialTheme:

MaterialTheme(
    colors = if (darkTheme) DarkColors else LightColors
) {
    // app content
}

שימוש בצבעי עיצוב

אפשר לאחזר את Colors שסופק ל-MaterialTheme באמצעות MaterialTheme.colors.

Text(
    text = "Hello theming",
    color = MaterialTheme.colors.primary
)

הצבע של פני השטח והתוכן

רכיבים רבים מקבלים צבע וצבע תוכן:

Surface(
    color = MaterialTheme.colors.surface,
    contentColor = contentColorFor(color),
    // ...
) { /* ... */ }

TopAppBar(
    backgroundColor = MaterialTheme.colors.primarySurface,
    contentColor = contentColorFor(backgroundColor),
    // ...
) { /* ... */ }

כך תוכלו לא רק להגדיר את הצבע של רכיב ה-Composable, אלא גם לספק צבע ברירת מחדל לתוכן, לרכיבי ה-Composable שמכיל. הרבה בתכנים קומפוזביליים משתמשים בצבע התוכן הזה כברירת מחדל. לדוגמה, הצבע של Text מבוסס על צבע התוכן של האב, ו-Icon משתמש בצבע הזה כדי להגדיר את הגוון שלו.

שתי דוגמאות לאותו באנר, בצבעים שונים

איור 3. הגדרת צבעי רקע שונים יוצרת צבעים שונים של טקסט וסמלים.

contentColorFor() מאחזרת את ה-"on" המתאים לכל צבעי העיצוב. לדוגמה, אם מגדירים צבע רקע primary ב-Surface, המערכת משתמשת בפונקציה הזו כדי להגדיר את onPrimary כצבע התוכן. אם מגדירים צבע רקע שאינו צבע העיצוב, צריך לציין גם צבע תוכן מתאים. שימוש ב-LocalContentColor כדי לאחזר את צבע התוכן המועדף לרקע הנוכחי, במיקום נתון בהיררכיה.

אלפא של תוכן

לרוב כדאי לשנות את מידת הדגשת התוכן כדי להעביר את מידת החשיבות שלו ולספק היררכיה חזותית. ההמלצות לגבי ניראות הטקסט ב-Material Design ממליצות להשתמש ברמות שונות של שקיפות כדי להעביר רמות שונות של חשיבות.

ב-Jetpack פיתוח נייטיב הדבר מיושם באמצעות LocalContentAlpha. כדי לציין אלפא של תוכן להיררכיה, מספקים ערך ל-CompositionLocal הזה. בתוך עץ תכנים קומפוזביליים יכולים להשתמש בערך הזה כדי להחיל את טיפול האלפא על התוכן שלהם. לדוגמה, כברירת מחדל, ב-Text וב-Icon נעשה שימוש בשילוב של LocalContentColor עם התאמה לשימוש ב-LocalContentAlpha. מאפיין החומר מציין כמה ערכים רגילים של אלפא (high,‏ medium,‏ disabled) שמתבססים על האובייקט ContentAlpha.

// By default, both Icon & Text use the combination of LocalContentColor &
// LocalContentAlpha. De-emphasize content by setting content alpha
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
    Text(
        // ...
    )
}
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.disabled) {
    Icon(
        // ...
    )
    Text(
        // ...
    )
}

למידע נוסף על CompositionLocal, כדאי לעיין בנתונים בהיקף מקומי עם מדריך מקומי של יצירה.

צילום מסך של כותרת מאמר, עם רמות טקסט שונות
הדגשה

איור 4. השתמשו ברמות שונות של הדגשה בטקסט כדי להעביר באופן חזותי את היררכיית המידע. שורת הטקסט הראשונה היא הכותרת, והיא מכילה את המידע החשוב ביותר, ולכן נעשה בה שימוש ב-ContentAlpha.high. השורה השנייה מכילה מטא-נתונים פחות חשובים, ולכן נעשה בה שימוש ב-ContentAlpha.medium.

עיצוב כהה

כדי להטמיע עיצוב בהיר ועיצוב כהה, מזינים קבוצות שונות של תכנים Colors לתוכן הקומפוזבילי MaterialTheme:

@Composable
fun MyTheme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    content: @Composable () -> Unit
) {
    MaterialTheme(
        colors = if (darkTheme) DarkColors else LightColors,
        /*...*/
        content = content
    )
}

בדוגמה הזו, MaterialTheme מוקף בפונקציה קומפוזבילית משלו, שמקבל פרמטר שמציין אם להשתמש בעיצוב כהה או לא. לחשבון במקרה הזה, הפונקציה מקבלת את ערך ברירת המחדל של darkTheme על ידי שליחת שאילתה על הגדרת העיצוב של המכשיר.

אפשר להשתמש בקוד כמו בדוגמה הבאה כדי לבדוק אם הערכים של Colors הנוכחיים הם בהירים או כהים:

val isLightTheme = MaterialTheme.colors.isLight
Icon(
    painterResource(
        id = if (isLightTheme) {
            R.drawable.ic_sun_24
        } else {
            R.drawable.ic_moon_24
        }
    ),
    contentDescription = "Theme"
)

שכבות-על

בעיצוב Material, משטחים בעיצובים כהים עם רמות גבוהות יותר מקבלים שכבות-על של רמה, שמבהירות את הרקע שלהם. ככל שהמשטח גבוה יותר (ככל שהוא קרוב יותר למקור אור משתמע), הוא יהפוך לבהיר יותר.

שכבות-העל האלה חלות באופן אוטומטי על ה-composable Surface כשמשתמשים בצבעים כהים, ועל כל composable אחר של Material שבו נעשה שימוש בשטח:

Surface(
    elevation = 2.dp,
    color = MaterialTheme.colors.surface, // color will be adjusted for elevation
    /*...*/
) { /*...*/ }

צילום מסך של אפליקציה, שבו מוצגים הצבעים השונים במעט שנעשה בהם שימוש לרכיבים ברמות גובה שונות

איור 5. הכרטיסים והניווט התחתון כוללים את הצבע surface בתור הרקע שלהם. מכיוון שהכרטיסים והניווט התחתון שונים זה מזה רמות גובה מעל הרקע, הן שונות במקצת צבעים - הכרטיסים בהירים יותר מהרקע והניווט התחתון קל יותר מהקלפים.

בתרחישים בהתאמה אישית שלא כוללים Surface, צריך להשתמש בפונקציה LocalElevationOverlay, CompositionLocal שמכיל את ElevationOverlay בשימוש על ידי Surface רכיבים:

// Elevation overlays
// Implemented in Surface (and any components that use it)
val color = MaterialTheme.colors.surface
val elevation = 4.dp
val overlaidColor = LocalElevationOverlay.current?.apply(
    color, elevation
)

כדי להשבית שכבות-על של הרשאות גישה, מציינים את הערך null בנקודה הרצויה בהיררכיה שניתן ליצור ממנה קומפוזיציות:

MyTheme {
    CompositionLocalProvider(LocalElevationOverlay provides null) {
        // Content without elevation overlays
    }
}

הגבלה של סימוני צבע

ב-Material מומלץ להשתמש בצבעים מודגשים מוגבלים בעיצובים כהים, ולרוב עדיף להשתמש בצבע surface במקום בצבע primary. רכיבים מורכבים של Material, כמו TopAppBar ו-BottomNavigation, מטמיעים את ההתנהגות הזו כברירת מחדל.

איור 6. עיצוב חומר כהה עם נגיעות צבע מוגבלות. בסרגל האפליקציות העליון נעשה שימוש בצבע הראשי בעיצוב בהיר, ובצבע פני השטח בעיצוב כהה.

לתרחישים מותאמים אישית, השתמשו primarySurface מאפיין לתוסף:

Surface(
    // Switches between primary in light theme and surface in dark theme
    color = MaterialTheme.colors.primarySurface,
    /*...*/
) { /*...*/ }

טיפוגרפיה

ב-Material מוגדר סוג מערכת, שמעודד אתכם להשתמש במספר קטן של סגנונות עם שמות סמנטיים.

דוגמה לכמה גופנים שונים בסגנונות שונים

איור 7. המערכת של סוג החומר.

מערכת Compose מטמיעה את מערכת הסוגים באמצעות המחלקות Typography, TextStyle וfont-related. ה-constructor של Typography מציע הגדרות ברירת מחדל לכל סגנון, כך שתוכלו להשמיט סגנונות שאתם לא רוצים להתאים אישית:

val raleway = FontFamily(
    Font(R.font.raleway_regular),
    Font(R.font.raleway_medium, FontWeight.W500),
    Font(R.font.raleway_semibold, FontWeight.SemiBold)
)

val myTypography = Typography(
    h1 = TextStyle(
        fontFamily = raleway,
        fontWeight = FontWeight.W300,
        fontSize = 96.sp
    ),
    body1 = TextStyle(
        fontFamily = raleway,
        fontWeight = FontWeight.W600,
        fontSize = 16.sp
    )
    /*...*/
)
MaterialTheme(typography = myTypography, /*...*/) {
    /*...*/
}

אם רוצים להשתמש באותו משפחת גופנים, ציינו את defaultFontFamily parameter ולהשמיט את fontFamily של רכיבי TextStyle:

val typography = Typography(defaultFontFamily = raleway)
MaterialTheme(typography = typography, /*...*/) {
    /*...*/
}

שימוש בסגנונות טקסט

ניתן לגשת ל-TextStyle דרך MaterialTheme.typography. אחזור הערכים של TextStyle:

Text(
    text = "Subtitle2 styled",
    style = MaterialTheme.typography.subtitle2
)

צילום מסך שבו מוצגת תערובת של גופנים שונים למטרות שונות

איור 8. אפשר להשתמש במגוון גופנים וסגנונות כדי להביע את המותג שלכם.

צורה

מערכת הרכיבים של Material מגדירה מערכת צורות, שמאפשרת להגדיר צורות לרכיבים גדולים, בינוניים וקטנים.

הצגת מגוון צורות של Material Design

איור 9. מערכת הצורה מסוג Material Design.

Compose מקטע מיישם את מערכת הצורה עם Shapes, שמאפשרת מציינים CornerBasedShape לכל קטגוריית מידות:

val shapes = Shapes(
    small = RoundedCornerShape(percent = 50),
    medium = RoundedCornerShape(0f),
    large = CutCornerShape(
        topStart = 16.dp,
        topEnd = 0.dp,
        bottomEnd = 0.dp,
        bottomStart = 16.dp
    )
)

MaterialTheme(shapes = shapes, /*...*/) {
    /*...*/
}

הרבה רכיבים משתמשים בצורות האלה כברירת מחדל. לדוגמה, הערכים Button,‏ TextField ו-FloatingActionButton מוגדרים כברירת מחדל כקטנים, הערכים AlertDialog מוגדרים כברירת מחדל כבינוניים והערכים ModalDrawer מוגדרים כברירת מחדל כגדולים. המיפוי המלא מופיע במאמר העזרה בנושא סכמות של צורות.

שימוש בצורות

ניתן לגשת ל-Shape דרך MaterialTheme.shapes. אחזור של Shapes באמצעות קוד:

Surface(
    shape = MaterialTheme.shapes.medium, /*...*/
) {
    /*...*/
}

צילום מסך של אפליקציה שמשתמשת בצורות של Material כדי להעביר את המצב שבו נמצא אלמנט

איור 10. משתמשים בצורות כדי להביע מותג או מצב.

סגנונות ברירת מחדל

אין קונספט מקביל בשדה 'פיתוח' סגנונות ברירת מחדל מ-Android Views. כדי לספק פונקציונליות דומה, אפשר ליצור פונקציות מורכבות משלכם שאפשר לשלב ביניהן, שמקיפות רכיבי Material. לדוגמה, כדי ליצור סגנון של לחצנים, צריך לתחום לחצן בפונקציה קומפוזבילית משלכם, להגדיר באופן ישיר את הפרמטרים שרוצים לשנות ולחשוף אחרים כפרמטרים לתוכן הקומפוזבילי שמכיל את התוכן.

@Composable
fun MyButton(
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
    content: @Composable RowScope.() -> Unit
) {
    Button(
        colors = ButtonDefaults.buttonColors(
            backgroundColor = MaterialTheme.colors.secondary
        ),
        onClick = onClick,
        modifier = modifier,
        content = content
    )
}

שכבות-על של עיצוב

כדי ליצור את המקבילה לשכבות-על של עיצוב מ-Android Views ב-Compose, אפשר להטמיע רכיבי Compose של MaterialTheme. מכיוון שהצבעים, הגופנים והצורות מוגדרים כברירת מחדל ב-MaterialTheme לערך העיצוב הנוכחי, אם העיצוב מגדיר רק אחד מהפרמטרים האלה, הפרמטרים האחרים ימשיכו לשמור על ערכי ברירת המחדל שלהם.

בנוסף, כשעוברים ממסכים מבוססי-תצוגה ל-Compose, חשוב לשים לב לשימושים במאפיין android:theme. סביר להניח שדרושה לך MaterialTheme בחלק הזה של עץ ממשק המשתמש של הכתיבה.

בדוגמה הזו, במסך הפרטים נעשה שימוש ב-PinkTheme ברוב המסך, ואז ב-BlueTheme בקטע הקשור. ראו את צילום המסך והקוד בהמשך.

איור 11. עיצובים בתצוגת עץ.

@Composable
fun DetailsScreen(/* ... */) {
    PinkTheme {
        // other content
        RelatedSection()
    }
}

@Composable
fun RelatedSection(/* ... */) {
    BlueTheme {
        // content
    }
}

מצבי רכיבים

לרכיבי Material שאפשר לבצע איתם פעולות (לחיצה, החלפת מצב וכו') יכולים להיות מצבים חזותיים שונים. המצבים כוללים מופעל, מושבת, לוחץ וכו'.

לרכיבים מורכבים יש בדרך כלל פרמטר enabled. הגדרה של הערך false מונעת אינטראקציה, ומשנה מאפיינים כמו צבע וגובה כדי להעביר באופן חזותי את מצב הרכיב.

איור 12. לחצן עם enabled = true (שמאל) ו-enabled = false (ימין).

ברוב המקרים אפשר להסתמך על ברירות המחדל לערכים כמו צבע וגובה. אם רוצים להגדיר ערכים שנעשה בהם שימוש במצבים שונים, יש זמינות כיתות ופונקציות נוחות. לחצן לדוגמה:

Button(
    onClick = { /* ... */ },
    enabled = true,
    // Custom colors for different states
    colors = ButtonDefaults.buttonColors(
        backgroundColor = MaterialTheme.colors.secondary,
        disabledBackgroundColor = MaterialTheme.colors.onBackground
            .copy(alpha = 0.2f)
            .compositeOver(MaterialTheme.colors.background)
        // Also contentColor and disabledContentColor
    ),
    // Custom elevation for different states
    elevation = ButtonDefaults.elevation(
        defaultElevation = 8.dp,
        disabledElevation = 2.dp,
        // Also pressedElevation
    )
) { /* ... */ }

איור 13. לחצן עם enabled = true (שמאל) ו-enabled = false (ימין), עם ערכי צבע וגובה מותאמים.

אדוות

רכיבי Material משתמשים בתנודות כדי לציין שהמשתמשים מבצעים איתם אינטראקציה. אם המיקום נעשה שימוש ב-MaterialTheme בהיררכיה, Ripple ישמש בתור ברירת מחדלIndication בתוך מגבילים כמו clickable וגם indication.

ברוב המקרים אפשר להסתמך על ברירת המחדל Ripple. אם תרצו להגדיר תוכלו להשתמש במראה שלהם RippleTheme כדי לשנות מאפיינים כמו צבע ואלפא.

אפשר להרחיב את RippleTheme ולהשתמש בפונקציות השירות defaultRippleColor ו-defaultRippleAlpha. לאחר מכן, אפשר לספק את העיצוב של 'גלי Google' בהתאמה אישית בהיררכיה באמצעות LocalRippleTheme

@Composable
fun MyApp() {
    MaterialTheme {
        CompositionLocalProvider(
            LocalRippleTheme provides SecondaryRippleTheme
        ) {
            // App content
        }
    }
}

@Immutable
private object SecondaryRippleTheme : RippleTheme {
    @Composable
    override fun defaultColor() = RippleTheme.defaultRippleColor(
        contentColor = MaterialTheme.colors.secondary,
        lightTheme = MaterialTheme.colors.isLight
    )

    @Composable
    override fun rippleAlpha() = RippleTheme.defaultRippleAlpha(
        contentColor = MaterialTheme.colors.secondary,
        lightTheme = MaterialTheme.colors.isLight
    )
}

alt_text

איור 14. לחצנים עם ערכי הדים שונים שסופקו דרך RippleTheme.

מידע נוסף

למידע נוסף על נושאי Material ב-Compose, תוכלו לעיין במקורות המידע הנוספים הבאים.

Codelabs

סרטונים