שינוי עיצוב ב'כתיבה' בעזרת Material 3

1. מבוא

בשיעור Codelab הזה נלמד איך להחיל ערכות נושא על אפליקציות ב-Jetpack Compose באמצעות Material Design 3. תלמדו גם על אבני הבניין העיקריות של ערכות הצבעים, הטיפוגרפיה והצורות ב-Material Design 3, שיעזרו לכם להחיל עיצוב על האפליקציה בדרכים מותאמות אישית ונגישות.

בנוסף, תלמדו על תמיכה בשינוי דינמי של ערכות נושא ועל רמות שונות של הדגשה.

מה תלמדו

ב-codelab הזה תלמדו:

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

מה תפַתחו

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

d15db3dc75a9d00f.png

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

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

1357cdbfaaa67721.png

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

1357cdbfaaa67721.png

נקודת הסיום של ה-codelab בנושא עיצוב ערכות נושא, עם עיצוב ערכת נושא כהה ועיצוב ערכת נושא דינמית כהה.

מה צריך

2. תהליך ההגדרה

בשלב הזה, מורידים את הקוד המלא של אפליקציית Reply שמעצבים ב-codelab הזה.

קבל את הקוד

הקוד של ה-Codelab הזה נמצא במאגר GitHub‏ android-compose-codelabs. כדי לשכפל אותו, מריצים את הפקודה:

$ git clone https://github.com/googlecodelabs/android-compose-codelabs

לחלופין, אפשר להוריד שני קובצי ZIP:

בדיקת האפליקציה לדוגמה

הקוד שהורדתם עכשיו מכיל קוד לכל ה-codelabs הזמינים של Compose. כדי להשלים את ה-Codelab הזה, פותחים את הפרויקט ThemingCodelab ב-Android Studio.

מומלץ להתחיל עם הקוד בענף הראשי ולעבור על השלבים ב-codelab בקצב שלכם. בכל שלב, אפשר להריץ כל אחת מהגרסאות ב-Android Studio על ידי שינוי הענף של git בפרויקט.

הסבר על קוד ההתחלה

הקוד הראשי מכיל חבילת ממשק משתמש, שכוללת את החבילות והקבצים העיקריים הבאים שבהם תשתמשו:

  • ‫MainActivity.kt – פעילות בנקודת הכניסה שבה מתחילים את אפליקציית Reply.
  • ‫com.example.reply.ui.theme – החבילה הזו מכילה עיצובים, טיפוגרפיה וערכות צבעים. בחבילה הזו תוסיפו ערכת נושא של Material.
  • ‫com.example.reply.ui.components – מכיל את הרכיבים המותאמים אישית של האפליקציה, כמו פריטים ברשימה, סרגלי אפליקציות וכו'. תחילת העיצובים על הרכיבים האלה.
  • ‫ReplyApp.kt – זו הפונקציה הקומפוזבילית הראשית שבה יתחיל עץ ממשק המשתמש. תחילו את העיצוב ברמה העליונה בקובץ הזה.

ב-Codelab הזה נתמקד בקובצי חבילה של ui.

3. התאמת העיצוב של Material 3

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

עיצוב Material 3 כולל את מערכות המשנה הבאות להוספת עיצוב לאפליקציה: סכמת צבעים, טיפוגרפיה ו- shapes (צורות). כשמשנים את הערכים האלה, השינויים משתקפים באופן אוטומטי ברכיבי M3 שבהם משתמשים כדי ליצור את האפליקציה. נסביר על כל אחת ממערכות המשנה ונראה איך מטמיעים אותה באפליקציה לדוגמה.

מערכות משנה של עיצוב Material: צבע, טיפוגרפיה וצורות.

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

4. ערכות של צבעים

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

חמישה צבעי בסיס ליצירת ערכת נושא M3.

חמישה צבעי מפתח בסיסיים ליצירת ערכת נושא של M3.

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

ארבעה גוונים של צבעים משניים, שלישוניים וצבעי בסיס משניים.

ארבעה צבעים טונאליים של צבעי בסיס ראשיים, משניים ושלישוניים.

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

ארבעה צבעים מונוכרומטיים של צבעים ניטרליים בסיסיים.

ארבעה צבעים טונאליים של צבעים ניטרליים בסיסיים.

מידע נוסף על סכמת צבעים ותפקידי צבעים

יצירת ערכות צבעים

אפשר ליצור ColorScheme מותאם אישית באופן ידני, אבל לרוב קל יותר ליצור אותו באמצעות צבעי מקור מהמותג. אפשר לעשות את זה באמצעות הכלי Material Theme Builder, ואפשר גם לייצא קוד של ערכות נושא של Compose.

אתם יכולים לבחור כל צבע שתרצו, אבל בתרחיש השימוש שלנו תשתמשו בצבע הראשי של התשובה שמוגדר כברירת מחדל #825500. בקטע Core colors (צבעים מרכזיים) שבצד ימין, לוחצים על Primary color (הצבע הראשי) ומוסיפים את הקוד בכלי לבחירת צבעים.

294f73fc9d2a570e.png

הוספת קוד הצבע הראשי בכלי Material Theme Builder.

אחרי שמוסיפים את הצבע הראשי בכלי ליצירת עיצוב Material, העיצוב הבא אמור להופיע בפינה השמאלית העליונה, עם אפשרות לייצא אותו. בשיעור Codelab הזה נסביר איך לייצא את ערכת העיצוב ב-Jetpack Compose.

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

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

הצבע הראשי #825500 יוצר את העיצוב הבא שתוסיפו לאפליקציה. בספריית Material 3 יש מגוון רחב של תפקידי צבעים שמאפשרים להביע באופן גמיש את המצב, הבולטות וההדגשה של רכיב.

ייצאנו ערכת צבעים בהירה וכהה מהצבע הראשי.

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

The Color.kt הקובץ שנוצר מכיל את הצבעים של העיצוב עם כל התפקידים שמוגדרים לצבעי העיצוב במצב בהיר ובמצב כהה.

Color.kt

package com.example.reply.ui.theme
import androidx.compose.ui.graphics.Color

val md_theme_light_primary = Color(0xFF825500)
val md_theme_light_onPrimary = Color(0xFFFFFFFF)
val md_theme_light_primaryContainer = Color(0xFFFFDDB3)
val md_theme_light_onPrimaryContainer = Color(0xFF291800)
val md_theme_light_secondary = Color(0xFF6F5B40)
val md_theme_light_onSecondary = Color(0xFFFFFFFF)
val md_theme_light_secondaryContainer = Color(0xFFFBDEBC)
val md_theme_light_onSecondaryContainer = Color(0xFF271904)
val md_theme_light_tertiary = Color(0xFF51643F)
val md_theme_light_onTertiary = Color(0xFFFFFFFF)
val md_theme_light_tertiaryContainer = Color(0xFFD4EABB)
val md_theme_light_onTertiaryContainer = Color(0xFF102004)
val md_theme_light_error = Color(0xFFBA1A1A)
val md_theme_light_errorContainer = Color(0xFFFFDAD6)
val md_theme_light_onError = Color(0xFFFFFFFF)
val md_theme_light_onErrorContainer = Color(0xFF410002)
val md_theme_light_background = Color(0xFFFFFBFF)
val md_theme_light_onBackground = Color(0xFF1F1B16)
val md_theme_light_surface = Color(0xFFFFFBFF)
val md_theme_light_onSurface = Color(0xFF1F1B16)
val md_theme_light_surfaceVariant = Color(0xFFF0E0CF)
val md_theme_light_onSurfaceVariant = Color(0xFF4F4539)
val md_theme_light_outline = Color(0xFF817567)
val md_theme_light_inverseOnSurface = Color(0xFFF9EFE7)
val md_theme_light_inverseSurface = Color(0xFF34302A)
val md_theme_light_inversePrimary = Color(0xFFFFB951)
val md_theme_light_shadow = Color(0xFF000000)
val md_theme_light_surfaceTint = Color(0xFF825500)
val md_theme_light_outlineVariant = Color(0xFFD3C4B4)
val md_theme_light_scrim = Color(0xFF000000)

val md_theme_dark_primary = Color(0xFFFFB951)
val md_theme_dark_onPrimary = Color(0xFF452B00)
val md_theme_dark_primaryContainer = Color(0xFF633F00)
val md_theme_dark_onPrimaryContainer = Color(0xFFFFDDB3)
val md_theme_dark_secondary = Color(0xFFDDC2A1)
val md_theme_dark_onSecondary = Color(0xFF3E2D16)
val md_theme_dark_secondaryContainer = Color(0xFF56442A)
val md_theme_dark_onSecondaryContainer = Color(0xFFFBDEBC)
val md_theme_dark_tertiary = Color(0xFFB8CEA1)
val md_theme_dark_onTertiary = Color(0xFF243515)
val md_theme_dark_tertiaryContainer = Color(0xFF3A4C2A)
val md_theme_dark_onTertiaryContainer = Color(0xFFD4EABB)
val md_theme_dark_error = Color(0xFFFFB4AB)
val md_theme_dark_errorContainer = Color(0xFF93000A)
val md_theme_dark_onError = Color(0xFF690005)
val md_theme_dark_onErrorContainer = Color(0xFFFFDAD6)
val md_theme_dark_background = Color(0xFF1F1B16)
val md_theme_dark_onBackground = Color(0xFFEAE1D9)
val md_theme_dark_surface = Color(0xFF1F1B16)
val md_theme_dark_onSurface = Color(0xFFEAE1D9)
val md_theme_dark_surfaceVariant = Color(0xFF4F4539)
val md_theme_dark_onSurfaceVariant = Color(0xFFD3C4B4)
val md_theme_dark_outline = Color(0xFF9C8F80)
val md_theme_dark_inverseOnSurface = Color(0xFF1F1B16)
val md_theme_dark_inverseSurface = Color(0xFFEAE1D9)
val md_theme_dark_inversePrimary = Color(0xFF825500)
val md_theme_dark_shadow = Color(0xFF000000)
val md_theme_dark_surfaceTint = Color(0xFFFFB951)
val md_theme_dark_outlineVariant = Color(0xFF4F4539)
val md_theme_dark_scrim = Color(0xFF000000)


val seed = Color(0xFF825500)

The Theme.kt הקובץ שנוצר מכיל הגדרה של ערכות צבעים בהירות וכהות ושל העיצוב של האפליקציה. הוא מכיל גם את הפונקציה הקומפוזבילית העיקרית של העיצוב, AppTheme().

Theme.kt

package com.example.reply.ui.theme

import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.runtime.Composable


private val LightColors = lightColorScheme(
   primary = md_theme_light_primary,
   onPrimary = md_theme_light_onPrimary,
   primaryContainer = md_theme_light_primaryContainer,
   onPrimaryContainer = md_theme_light_onPrimaryContainer,
   secondary = md_theme_light_secondary,
   onSecondary = md_theme_light_onSecondary,
   secondaryContainer = md_theme_light_secondaryContainer,
   onSecondaryContainer = md_theme_light_onSecondaryContainer,
   tertiary = md_theme_light_tertiary,
   onTertiary = md_theme_light_onTertiary,
   tertiaryContainer = md_theme_light_tertiaryContainer,
   onTertiaryContainer = md_theme_light_onTertiaryContainer,
   error = md_theme_light_error,
   errorContainer = md_theme_light_errorContainer,
   onError = md_theme_light_onError,
   onErrorContainer = md_theme_light_onErrorContainer,
   background = md_theme_light_background,
   onBackground = md_theme_light_onBackground,
   surface = md_theme_light_surface,
   onSurface = md_theme_light_onSurface,
   surfaceVariant = md_theme_light_surfaceVariant,
   onSurfaceVariant = md_theme_light_onSurfaceVariant,
   outline = md_theme_light_outline,
   inverseOnSurface = md_theme_light_inverseOnSurface,
   inverseSurface = md_theme_light_inverseSurface,
   inversePrimary = md_theme_light_inversePrimary,
   surfaceTint = md_theme_light_surfaceTint,
   outlineVariant = md_theme_light_outlineVariant,
   scrim = md_theme_light_scrim,
)


private val DarkColors = darkColorScheme(
   primary = md_theme_dark_primary,
   onPrimary = md_theme_dark_onPrimary,
   primaryContainer = md_theme_dark_primaryContainer,
   onPrimaryContainer = md_theme_dark_onPrimaryContainer,
   secondary = md_theme_dark_secondary,
   onSecondary = md_theme_dark_onSecondary,
   secondaryContainer = md_theme_dark_secondaryContainer,
   onSecondaryContainer = md_theme_dark_onSecondaryContainer,
   tertiary = md_theme_dark_tertiary,
   onTertiary = md_theme_dark_onTertiary,
   tertiaryContainer = md_theme_dark_tertiaryContainer,
   onTertiaryContainer = md_theme_dark_onTertiaryContainer,
   error = md_theme_dark_error,
   errorContainer = md_theme_dark_errorContainer,
   onError = md_theme_dark_onError,
   onErrorContainer = md_theme_dark_onErrorContainer,
   background = md_theme_dark_background,
   onBackground = md_theme_dark_onBackground,
   surface = md_theme_dark_surface,
   onSurface = md_theme_dark_onSurface,
   surfaceVariant = md_theme_dark_surfaceVariant,
   onSurfaceVariant = md_theme_dark_onSurfaceVariant,
   outline = md_theme_dark_outline,
   inverseOnSurface = md_theme_dark_inverseOnSurface,
   inverseSurface = md_theme_dark_inverseSurface,
   inversePrimary = md_theme_dark_inversePrimary,
   surfaceTint = md_theme_dark_surfaceTint,
   outlineVariant = md_theme_dark_outlineVariant,
   scrim = md_theme_dark_scrim,
)

@Composable
fun AppTheme(
   useDarkTheme: Boolean = isSystemInDarkTheme(),
   content: @Composable() () -> Unit
) {
   val colors = if (!useDarkTheme) {
       LightColors
   } else {
       DarkColors
   }

   MaterialTheme(
       colorScheme = colors,
       content = content
   )
}

הרכיב המרכזי להטמעת עיצוב ב-Jetpack Compose הוא הקומפוזבילי MaterialTheme.

עוטפים את ה-composable MaterialTheme() בפונקציה AppTheme(), שמקבלת שני פרמטרים:

  • ‫useDarkTheme – הפרמטר הזה קשור לפונקציה isSystemInDarkTheme() כדי להתאים את העיצוב להגדרות העיצוב של המערכת ולהחיל את העיצוב הבהיר או הכהה. אם רוצים להגדיר ידנית את העיצוב של האפליקציה כבהיר או כהה, אפשר להעביר ערך בוליאני ל-useDarkTheme.
  • ‫content – התוכן שיוחל עליו העיצוב.

Theme.kt

@Composable
fun AppTheme(
   useDarkTheme: Boolean = isSystemInDarkTheme(),
   content: @Composable() () -> Unit
) {
   val colors = if (!useDarkTheme) {
       LightColors
   } else {
       DarkColors
   }

   MaterialTheme(
       colorScheme = colors,
       content = content
   )
}

אם תנסו להריץ את האפליקציה עכשיו, תראו שהיא נראית אותו דבר. למרות שייבאתם את ערכת הצבעים החדשה שלנו עם צבעי עיצוב חדשים, אתם עדיין רואים את עיצוב הבסיס כי לא החלתם את העיצוב על אפליקציית Compose.

אפליקציה עם עיצוב בסיסי כשלא מוחל עיצוב.

אפליקציה עם עיצוב בסיסי כשלא מוחל עיצוב.

כדי להחיל את העיצוב החדש, ב-MainActivity.kt, עוטפים את הקומפוזבילית הראשית ReplyApp בפונקציית העיצוב הראשית, AppTheme().

MainActivity.kt

setContent {
   val uiState by viewModel.uiState.collectAsStateWithLifecycle()

   AppTheme {
       ReplyApp(/*..*/)
   }
}

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

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

MainActivity.kt

@Preview(
   uiMode = Configuration.UI_MODE_NIGHT_YES,
   name = "DefaultPreviewDark"
)
@Preview(
   uiMode = Configuration.UI_MODE_NIGHT_NO,
   name = "DefaultPreviewLight"
)
@Composable
fun ReplyAppPreview() {
   AppTheme {
       ReplyApp(
           replyHomeUIState = ReplyHomeUIState(
               emails = LocalEmailsDataProvider.allEmails
           )
       )
   }
}

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

fddf7b9cc99b1fe3.png be7a661b4553167b.png

אפליקציה עם עיצוב בסיסי (מימין).

אפליקציה עם ערכת צבעים מיובאת (מימין).

674cec6cc12db6a0.png

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

‫Material 3 תומך גם בסכמות צבעים בהירות וגם בסכמות צבעים כהות. השתמשתם רק בערכת הנושא המיובאת כדי לעטוף את האפליקציה, ורכיבי Material 3 משתמשים בתפקידי צבע ברירת המחדל.

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

תפקידי צבעים ונגישות

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

1f184a05ea57aa84.png

תפקידי הצבעים: צבע ראשי, צבע משני וצבע שלישוני.

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

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

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

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

מידע נוסף זמין במאמר בנושא תפקידי צבעים ונגישות.

הגבהות טונאליות וצלליות

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

הגבהה טונאלית עם הגבהה מוצללת הגבהה טונאלית ברמה 2, שמשתמשת בצבע מתוך משבצת הצבע הראשי.

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

‫M3 Surface – רכיב ה-Composable שמאחורי רוב רכיבי M3 – כולל תמיכה בהגבהה טונאלית ובהגבהת צל:

Surface(
   modifier = modifier,
   tonalElevation = {..}
   shadowElevation = {..}
) {
   Column(content = content)
}

הוספת צבעים לאפליקציה

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

be7a661b4553167b.png

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

צבעי המשטח

במסך הבית, מתחילים בצירוף של הקומפוזיציה הראשית של האפליקציה ב-Surface() כדי לספק את הבסיס למיקום התוכן של האפליקציה מעליה. פותחים את MainActivity.kt ועוטפים את הקומפוזיציה ReplyApp() באמצעות Surface.

בנוסף, תציינו גובה טונאלי של 5dp כדי לתת לרקע צבע טונאלי של משבצת ראשית, שיעזור ליצור ניגודיות ביחס לפריט ברשימה ולסרגל החיפוש שמעליו. כברירת מחדל, הגובה הטונאלי והגובה של הצל על פני השטח הם 0.dp.

MainActivity.kt

AppTheme {
   Surface(tonalElevation = 5.dp) {
       ReplyApp(
           replyHomeUIState = uiState,
          // other parameters
         )
   }
}

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

be7a661b4553167b.png e70d762495173610.png

רקע האפליקציה ללא צבע משטח וצבע טונאלי (מימין).

רקע האפליקציה עם רכיב Surface וצבע טונאלי (מימין).

הצבעים של סרגל האפליקציה

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

5779fc399d8a8187.png

סרגל חיפוש מותאם אישית ללא רקע (מימין).

סרגל חיפוש בהתאמה אישית עם רקע (מימין).

עכשיו עורכים את ui/components/ReplyAppBars.kt, שמכיל את סרגל האפליקציות. תוסיף את MaterialTheme.colorScheme.background ל-Modifier של Row Composable.

ReplyAppBars.kt

@Composable
fun ReplySearchBar(modifier: Modifier = Modifier) {
   Row(
       modifier = modifier
           .fillMaxWidth()
           .padding(16.dp)
           .background(MaterialTheme.colorScheme.background),
       verticalAlignment = Alignment.CenterVertically
   ) {
       // Search bar content
   }
}

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

b1b374b801dadc06.png

סרגל חיפוש עם צבע רקע מעל משטח טונאלי.

צבעים של כפתור פעולה צף (FAB)

70ceac87233fe466.png

‫FAB גדול ללא ערכת נושא (משמאל).

לחצן פעולה צף גדול עם עיצוב וצבע משני (מימין).

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

בקובץ ReplyListContent.kt מעדכנים את containerColor של לחצן ה-FAB לצבע tertiaryContainer ואת צבע התוכן לצבע onTertiaryContainer כדי לשמור על הנגישות ועל ניגודיות הצבעים.

ReplyListContent.kt

ReplyInboxScreen(/*..*/) {
// Email list content
  LargeFloatingActionButton(
    containerColor = MaterialTheme.colorScheme.tertiaryContainer,
    contentColor = MaterialTheme.colorScheme.onTertiaryContainer
  ){
   /*..*/   
  }
}

מריצים את האפליקציה כדי לראות את לחצן ה-FAB עם העיצוב החדש. ב-Codelab הזה משתמשים ב-LargeFloatingActionButton.

צבעי הכרטיסים

רשימת האימיילים במסך הבית משתמשת ברכיב כרטיס. כברירת מחדל, זהו כרטיס עם מילוי שמשתמש בצבע וריאציה של המשטח לצבע המאגר, כדי ליצור הפרדה ברורה בין צבע המשטח לצבע הכרטיס. ‫Compose מספקת גם הטמעות של ElevatedCard ושל OutlinedCard.

כדי להדגיש עוד יותר פריטים חשובים, אפשר לספק גוונים משניים. תשנו את ui/components/ReplyEmailListItem.kt על ידי עדכון הצבע של מאגר הכרטיסים באמצעות CardDefaults.cardColors() עבור אימיילים חשובים:

ReplyEmailListItem.kt

Card(
   modifier =  modifier
       .padding(horizontal = 16.dp, vertical = 4.dp)
       .semantics { selected = isSelected }
       .clickable { navigateToDetail(email.id) },
   colors = CardDefaults.cardColors(
       containerColor = if (email.isImportant)
           MaterialTheme.colorScheme.secondaryContainer
       else MaterialTheme.colorScheme.surfaceVariant
   )
){
  /*..*/   
}

5818200be0b01583.png 9367d40023db371d.png

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

צבע של פריט ברשימת פרטים

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

7a9ea7cf3e91e9c7.png 79b3874aeca4cd1.png

דף פרטים שמוגדר כברירת מחדל ללא פריט ברשימה עם עיצוב (מימין).

פריט ברשימת פרטים עם רקע של ערכת נושא (מימין).

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

ReplyEmailThreadItem.kt

@Composable
fun ReplyEmailThreadItem(
   email: Email,
   modifier: Modifier = Modifier
) {
   Column(
       modifier = modifier
           .fillMaxWidth()
           .padding(16.dp)
           .background(MaterialTheme.colorScheme.background)
           .padding(20.dp)
    ) {
      // List item content
    }
}

אפשר לראות שרק על ידי הוספת רקע, יש הפרדה ברורה בין משטח הצבע לבין פריט הרשימה.

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

5. הוספת צבעים דינמיים באפליקציה

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

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

התכונה 'צבע דינמי' זמינה ב-Android מגרסה 12 ואילך. אם האפשרות 'צבע דינמי' זמינה, אתם יכולים להגדיר ערכת צבעים דינמית באמצעות dynamicDarkColorScheme() או dynamicLightColorScheme(). אם לא, צריך לחזור לשימוש בColorScheme בהיר או כהה שמוגדר כברירת מחדל.

מחליפים את הקוד של הפונקציה AppTheme בקובץ Theme.kt בקוד שבהמשך:

Theme.kt

@Composable
fun AppTheme(
   useDarkTheme: Boolean =  isSystemInDarkTheme(),
   content: @Composable () -> Unit
) {
   val context = LocalContext.current
   val colors = when {
       (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) -> {
           if (useDarkTheme) dynamicDarkColorScheme(context)
           else dynamicLightColorScheme(context)
       }
       useDarkTheme -> DarkColors
       else -> LightColors
   }
   
      MaterialTheme(
       colorScheme = colors,
       content = content
     )
}

fecc63b4c6034236.png

עיצוב דינמי שנלקח מהטפט של Android 13.

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

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

1095e2b2c1ffdc14.png

אפליקציה ללא צבע בשורת הסטטוס (מימין).

אפליקציה עם צבע שורת הסטטוס (מימין).

כדי לעדכן את צבע סרגל הסטטוס בהתאם לצבע הראשי של העיצוב, מוסיפים את צבע סרגל הסטטוס אחרי בחירת ערכת הצבעים ב-AppTheme composable:

Theme.kt

@Composable
fun AppTheme(
   useDarkTheme: Boolean =  isSystemInDarkTheme(),
   content: @Composable () -> Unit
) {
 
 // color scheme selection code

 // Add primary status bar color from chosen color scheme.
 val view = LocalView.current
 if (!view.isInEditMode) {
    SideEffect {
        val window = (view.context as Activity).window
        window.statusBarColor = colors.primary.toArgb()
        WindowCompat
            .getInsetsController(window, view)
            .isAppearanceLightStatusBars = useDarkTheme
    }
 }
   
  MaterialTheme(
    colorScheme = colors,
     content = content
   )
}

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

69093b5bce31fd43.png

עיצוב בהיר דינמי (משמאל) ועיצוב כהה דינמי (מימין) שמוחל עם טפט ברירת המחדל של Android 13.

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

6. טיפוגרפיה

ב-Material Design 3 מוגדר קנה מידה של סוגים. השמות והקיבוץ פושטו: display (תצוגה), headline (כותרת), title (שם), body (גוף) ו-label (תווית), עם גדלים גדולים, בינוניים וקטנים לכל אחד מהם.

999a161dcd9b0ec4.png

סולם סוגים של Material 3.

הגדרת טיפוגרפיה

‫Compose מספקת את המחלקה M3 Typography – יחד עם המחלקות הקיימות TextStyle ו- font-related – כדי ליצור מודל של סולם הסוגים של Material 3.

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

באפליקציה שלך נעשה שימוש בחמישה סגנונות טיפוגרפיים: headlineSmall, titleLarge, bodyLarge, bodyMedium ו-labelMedium. הסגנונות האלה יחולו גם על מסך הבית וגם על מסך הפרטים.

מסך שמציג את השימוש בטיפוגרפיה של כותרת, תווית וסגנון גוף.

מסך שבו מוצג שימוש בטיפוגרפיה של כותרת, תווית וסגנון גוף.

לאחר מכן, עוברים לחבילה ui/theme ופותחים את Type.kt. כדי לספק הטמעה משלכם לחלק מסגנונות הטקסט במקום ערכי ברירת המחדל, מוסיפים את הקוד הבא:

Type.kt

val typography = Typography(
   headlineSmall = TextStyle(
       fontWeight = FontWeight.SemiBold,
       fontSize = 24.sp,
       lineHeight = 32.sp,
       letterSpacing = 0.sp
   ),
   titleLarge = TextStyle(
       fontWeight = FontWeight.Normal,
       fontSize = 18.sp,
       lineHeight = 28.sp,
       letterSpacing = 0.sp
   ),
   bodyLarge = TextStyle(
       fontWeight = FontWeight.Normal,
       fontSize = 16.sp,
       lineHeight = 24.sp,
       letterSpacing = 0.15.sp
   ),
   bodyMedium = TextStyle(
       fontWeight = FontWeight.Medium,
       fontSize = 14.sp,
       lineHeight = 20.sp,
       letterSpacing = 0.25.sp
   ),
   labelMedium = TextStyle(
       fontWeight = FontWeight.SemiBold,
       fontSize = 12.sp,
       lineHeight = 16.sp,
       letterSpacing = 0.5.sp
   )
)

הגדרתם את הטיפוגרפיה. כדי להוסיף אותו לעיצוב, מעבירים אותו אל MaterialTheme() composable בתוך AppTheme:

Theme.kt

@Composable
fun AppTheme(
   useDarkTheme: Boolean = isSystemInDarkTheme(),
   content: @Composable() () -> Unit
) {
  // dynamic theming content

   MaterialTheme(
       colorScheme = colors,
       typography = typography,
       content = content
   )
}

עבודה עם טיפוגרפיה

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

Text(
   text = "Hello M3 theming",
   style = MaterialTheme.typography.titleLarge
)

Text(
   text = "you are learning typography",
   style = MaterialTheme.typography.bodyMedium
)

סביר להניח שלא תצטרכו את כל 15 הסגנונות שמוגדרים כברירת מחדל בסולם הסוגים של Material Design. ב-codelab הזה נבחרו חמישה גדלים, והשאר הושמטו.

מכיוון שלא החלתם טיפוגרפיה על רכיבי ה-Composable‏ (Text(), כל הטקסט חוזר ל-Typography.bodyLarge כברירת מחדל.

טיפוגרפיה של רשימת הבית

לאחר מכן, מחילים טיפוגרפיה על הפונקציה ReplyEmailListItem ב-ui/components/ReplyEmailListItem.kt כדי ליצור הבחנה בין כותרות לתוויות:

ReplyEmailListItem.kt

Text(
   text = email.sender.firstName,
   style = MaterialTheme.typography.labelMedium
)

Text(
   text = email.createdAt,
   style = MaterialTheme.typography.labelMedium
)

Text(
   text = email.subject,
   style = MaterialTheme.typography.titleLarge,
   modifier = Modifier.padding(top = 12.dp, bottom = 8.dp),
)

Text(
   text = email.body,
   maxLines = 2,
   style = MaterialTheme.typography.bodyLarge,
   overflow = TextOverflow.Ellipsis
)

90645c0765167bb7.png 6c4af2f412c18bfb.png

מסך הבית בלי טיפוגרפיה (מימין).

מסך הבית עם טיפוגרפיה (משמאל).

טיפוגרפיה של רשימת פרטים

באופן דומה, מוסיפים את הטיפוגרפיה במסך הפרטים על ידי עדכון כל רכיבי הטקסט של ReplyEmailThreadItem ב-ui/components/ReplyEmailThreadItem.kt:

ReplyEmailThreadItem.kt

Text(
   text = email.sender.firstName,
   style = MaterialTheme.typography.labelMedium
)

Text(
   text = stringResource(id = R.string.twenty_mins_ago),
   style = MaterialTheme.typography.labelMedium
)

Text(
   text = email.subject,
   style = MaterialTheme.typography.bodyMedium,
   modifier = Modifier.padding(top = 12.dp, bottom = 8.dp),
)

Text(
   text = email.body,
   style = MaterialTheme.typography.bodyLarge,
   color = MaterialTheme.colorScheme.onSurfaceVariant
)

543ac09e43d8761.png 3412771e95a45f36.png

מסך פרטים בלי טיפוגרפיה (מימין).

מסך פרטים עם טיפוגרפיה (מימין).

התאמה אישית של הטיפוגרפיה

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

תשנו את סגנון הטקסט בקובץ theme/Type.kt, והשינוי יחול על כל הרכיבים שמשתמשים בו.

עדכון של fontWeight ל-SemiBold ושל lineHeight ל-32.sp עבור titleLarge, שמשמש לנושא בפריט הרשימה. ההדגשה תהיה על הנושא, וההפרדה תהיה ברורה.

Type.kt

...
titleLarge = TextStyle(
   fontWeight = FontWeight.SemiBold,
   fontSize = 18.sp,
   lineHeight = 32.sp,
   letterSpacing = 0.0.sp
),
...

f8d2212819eb0b61.png

החלת טיפוגרפיה מותאמת אישית על טקסט הנושא.

7. צורות

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

הגדרת צורות

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

יש צורות בגדלים שונים בסולם הצורות:

  • קטן במיוחד
  • קטן
  • בינונית
  • גדול
  • גדול מאוד

כברירת מחדל, לכל צורה סגורה יש ערך ברירת מחדל שאפשר לשנות. באפליקציה שלכם, תשתמשו בצורה הבינונית כדי לשנות את הפריט ברשימה, אבל אתם יכולים להגדיר גם צורות אחרות. יוצרים קובץ חדש בשם Shape.kt בחבילה ui/theme ומוסיפים את הקוד לצורות:

Shape.kt

package com.example.reply.ui.theme

import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Shapes
import androidx.compose.ui.unit.dp

val shapes = Shapes(
   extraSmall = RoundedCornerShape(4.dp),
   small = RoundedCornerShape(8.dp),
   medium = RoundedCornerShape(16.dp),
   large = RoundedCornerShape(24.dp),
   extraLarge = RoundedCornerShape(32.dp)
)

אחרי שמגדירים את shapes, מעבירים אותו אל MaterialTheme M3 כמו שעשיתם לגבי צבעים וטיפוגרפיה:

Theme.kt

@Composable
fun AppTheme(
   useDarkTheme: Boolean = isSystemInDarkTheme(),
   content: @Composable() () -> Unit
) {
  // dynamic theming content

   MaterialTheme(
       colorScheme = colors,
       typography = typography,
       shapes = shapes
       content = content
   )
}

עבודה עם צורות

בדומה לצבע ולטיפוגרפיה, אפשר להחיל צורות על רכיבי Material באמצעות MaterialTheme.shape, וכך לקבל את מופע Shape כדי לגשת לצורות Material.

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

Card(shape = MaterialTheme.shapes.medium) { /* card content */ }
FloatingActionButton(shape = MaterialTheme.shapes.large) { /* fab content */}

ערכי ברירת מחדל של צורות לכל רכיבי Material 3. *מיפוי של רכיבי Material באמצעות צורות מסוגים שונים.*

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

יש עוד שתי צורות שאפשר להשתמש בהן – RectangleShape ו-CircleShape – שהן חלק מ'יצירה'. לצורה המלבנית אין רדיוס גבול, ולצורה העגולה יש קצוות מעוגלים מלאים.

אפשר גם להחיל צורה על רכיבים באמצעות Modifiers שמקבלים צורות, כמו Modifier.clip,‏ Modifier.background ו-Modifier.border.

הצורה של סרגל האפליקציה

אנחנו רוצים שרקע סרגל האפליקציה יהיה עם פינות מעוגלות:

f873392abe535494.png

האפליקציה TopAppBar משתמשת בRow עם צבע רקע. כדי ליצור רקע עם פינות מעוגלות, צריך להגדיר את צורת הרקע על ידי העברת CircleShape לשינוי הרקע:

ReplyAppBars.kt

@Composable
fun ReplySearchBar(modifier: Modifier = Modifier) {
   Row(
       modifier = modifier
           .fillMaxWidth()
           .padding(16.dp)
           .background(
               MaterialTheme.colorScheme.background,
               CircleShape
           ),
       verticalAlignment = Alignment.CenterVertically
   ) {
       // Search bar content
   }
}

f873392abe535494.png

צורה של פריט ברשימת פרטים

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

3412771e95a45f36.png 80ee881c41a98c2a.png

עמודה של פריט ברשימה מפורטת ללא צורה בפריט ברשימה (מימין) וצורה בינונית ברשימה (משמאל).

ReplyEmailThreadItem.kt

@Composable
fun ReplyEmailThreadItem(
   email: Email,
   modifier: Modifier = Modifier
) {
   Column(
       modifier = modifier
           .fillMaxWidth()
           .padding(8.dp)
           .background(
               MaterialTheme.colorScheme.background,
               MaterialTheme.shapes.medium
           )
           .padding(16.dp)

   ) {
      // List item content
      
   }
}

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

8. הדגשה

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

  1. שימוש בצבעים surface,‏ surface-variant ו-background לצד הצבעים on-surface ו-on-surface-variants ממערכת הצבעים המורחבת של M3.

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

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

תפקידי הצבע Surface,‏ Background ו-Surface variant.

תפקידי הצבע של המשטח, הרקע והמשטח.

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

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

תעדכנו את הצבע של הטקסט של השעה ושל גוף הטקסט של רכיב ה-Composable ל-onSurfaceVariant. כך ההדגשה שלו פחותה בהשוואה ל-onContainerColors, שמוחל על רכיבי טקסט של נושא וכותרת כברירת מחדל.

2c9b7f2bd016edb8.png 6850ff391f21e4ba.png

השעה וגוף הטקסט עם אותו דגש בהשוואה לנושא ולכותרת (מימין).

השעה והתוכן עם פחות הדגשה בהשוואה לנושא ולכותרת (מימין).

ReplyEmailListItem.kt

Text(
   text = email.createdAt,
   style = MaterialTheme.typography.labelMedium,
   color = MaterialTheme.colorScheme.onSurfaceVariant
)

Text(
   text = email.body,
   maxLines = 2,
   style = MaterialTheme.typography.bodyLarge,
   color = MaterialTheme.colorScheme.onSurfaceVariant
   overflow = TextOverflow.Ellipsis
)

בכרטיס של האימייל החשוב עם הרקע secondaryContainer, צבע הטקסט הוא onSecondaryContainer כברירת מחדל. בשאר האימיילים, הרקע הוא surfaceVariant, ולכן כל הטקסט מוגדר כברירת מחדל בצבע onSurfaceVariant.

9. מזל טוב

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

2d8fcabf15ac5202.png 5a4d31db0185dca6.png ce009e4ce560834d.png

סוף התוצאות של העיצוב עם צבעים דינמיים וערכת צבעים שהוחלו.

המאמרים הבאים

כדאי לעיין ב-Codelabs אחרים שלנו בנושא Compose: מסלול למידה

קריאה נוספת

אפליקציות לדוגמה

  • אפליקציית Reply sample עם התאמת עיצוב מלאה של Material 3
  • JetChat שמדגים שימוש בערכות נושא דינמיות

מסמכים לדוגמה