۱. مقدمه
در این آزمایشگاه کد، شما در مورد تمبندی برنامههای خود در Jetpack Compose با استفاده از Material Design 3 یاد خواهید گرفت. همچنین در مورد بلوکهای سازنده کلیدی طرحهای رنگی، تایپوگرافی و اشکال Material Design 3 خواهید آموخت که به شما کمک میکنند تا تمبندی برنامه خود را به روشهای شخصیسازی شده و در دسترس انجام دهید.
علاوه بر این، پشتیبانی از قالببندی پویا را به همراه سطوح تأکید مختلف بررسی خواهید کرد.
آنچه یاد خواهید گرفت
در این آزمایشگاه کد، شما یاد خواهید گرفت:
- جنبههای کلیدی قالببندی متریال ۳
- طرحهای رنگی متریال ۳ و نحوه تولید تم برای برنامه شما
- چگونه از تمهای پویا و روشن/تیره برای برنامه خود پشتیبانی کنیم؟
- تایپوگرافی و اشکال برای شخصیسازی برنامه شما
- کامپوننتهای متریال ۳ و سفارشیسازی برای استایلدهی به برنامه شما
آنچه خواهید ساخت
در این آزمایشگاه کد، شما یک برنامهی کلاینت ایمیل به نام Reply را تمگذاری خواهید کرد. شما با یک برنامهی بدون استایل، با استفاده از تم پایه شروع میکنید و آموختههای خود را برای تمگذاری برنامه و پشتیبانی از تمهای تیره اعمال خواهید کرد.

نقطه شروع پیشفرض برنامه ما با قالب پایه.
شما تم خود را با طرح رنگ، تایپوگرافی و شکلها ایجاد خواهید کرد و سپس آن را به لیست ایمیل و صفحه جزئیات برنامه خود اعمال خواهید کرد. همچنین پشتیبانی از تم پویا را به برنامه اضافه خواهید کرد. در پایان codelab، شما از هر دو تم رنگی و پویا برای برنامه خود پشتیبانی خواهید کرد.

نقطه پایان کدلب تمگذاری با تمگذاری رنگ روشن و تمگذاری پویای روشن.

نقطه پایان کدلب تمگذاری با تمگذاری رنگ تیره و تمگذاری پویای تیره.
آنچه شما نیاز خواهید داشت
- آخرین نسخه اندروید استودیو
- تجربه اولیه با زبان کاتلین
- درک اولیه از Jetpack Compose
- آشنایی اولیه با طرحبندیهای Compose، مانند Row ، Column و Modifier
۲. راهاندازی
در این مرحله، کد کامل برنامه Reply را که در این codelab استایلدهی خواهید کرد، دانلود میکنید.
کد را دریافت کنید
کد این codelab را میتوانید در مخزن گیتهاب android-compose-codelabs پیدا کنید. برای کپی کردن آن، دستور زیر را اجرا کنید:
$ git clone https://github.com/googlecodelabs/android-compose-codelabs
روش دیگر، میتوانید دو فایل زیپ را دانلود کنید:
نمونه برنامه را بررسی کنید
کدی که دانلود کردید شامل کد مربوط به تمام آزمایشگاههای کد Compose موجود است. برای تکمیل این آزمایشگاه کد، پروژه ThemingCodelab را در اندروید استودیو باز کنید.
توصیه میکنیم با کد موجود در شاخه اصلی شروع کنید و گام به گام codelab را با سرعت دلخواه خود دنبال کنید. در هر زمان، میتوانید با تغییر شاخه git پروژه، هر نسخه را در اندروید استودیو اجرا کنید.
بررسی کد شروع
کد اصلی شامل یک بسته رابط کاربری است که شامل بستهها و فایلهای اصلی زیر است که با آنها تعامل خواهید داشت:
-
MainActivity.kt- فعالیت نقطه ورود که در آن برنامه پاسخ را شروع میکنید. -
com.example.reply.ui.theme– این بسته شامل تمها، تایپوگرافی و طرحهای رنگی است. شما در این بسته تمبندی متریال را اضافه خواهید کرد. -
com.example.reply.ui.components- شامل کامپوننتهای سفارشی برنامه مانند List Items، App Bars و غیره است. شما تمها را به این کامپوننتها اعمال خواهید کرد. -
ReplyApp.kt- این تابع Composable اصلی ماست که درخت رابط کاربری از آن شروع میشود. شما قالببندی سطح بالا را در این فایل اعمال خواهید کرد.
این آزمایشگاه کد روی فایلهای بسته ui تمرکز خواهد کرد.
۳. متریال ۳ قالببندی
Jetpack Compose پیادهسازی از طراحی متریال - یک سیستم طراحی جامع برای ایجاد رابطهای دیجیتال - را ارائه میدهد. اجزای طراحی متریال (دکمهها، کارتها، سوئیچها و غیره) بر اساس قالببندی متریال ساخته شدهاند، که روشی سیستماتیک برای سفارشیسازی طراحی متریال برای انعکاس بهتر برند محصول شما است.
قالب متریال ۳ شامل زیرسیستمهای زیر برای افزودن قالب به برنامه شما است: طرح رنگ ، تایپوگرافی و شکلها . وقتی این مقادیر را سفارشی میکنید، تغییرات شما به طور خودکار در کامپوننتهای M3 که برای ساخت برنامه خود استفاده میکنید، منعکس میشود. بیایید به هر زیرسیستم بپردازیم و آن را در برنامه نمونه پیادهسازی کنیم.

زیرسیستم متریال ۳ شامل رنگها، تایپوگرافی و شکلها.
۴. طرحهای رنگی
پایه و اساس یک طرح رنگی، مجموعهای از پنج رنگ کلیدی است که هر کدام به یک پالت رنگی متشکل از ۱۳ تُن مربوط میشوند که توسط اجزای Material 3 استفاده میشوند.

پنج رنگ کلیدی پایه برای ایجاد تم M3.
سپس هر رنگ تأکیدی (اولیه، ثانویه و ثالثیه) در چهار رنگ سازگار با تُنهای مختلف برای جفت شدن، تعریف تأکید و بیان بصری ارائه شده است.

چهار رنگ تُنال از رنگهای تأکیدی پایه اولیه، ثانویه و ثالثیه.
به طور مشابه، رنگهای خنثی نیز به چهار تُن سازگار تقسیم میشوند که برای سطوح و پسزمینه استفاده میشوند. این تُنها همچنین برای تأکید بر آیکونهای متنی هنگام قرار دادن روی هر سطحی مهم هستند.

چهار رنگ تُنال از رنگهای خنثی پایه.
درباره طرح رنگ و نقشهای رنگ بیشتر بخوانید.
تولید طرحهای رنگی
اگرچه میتوانید یک ColorScheme سفارشی را به صورت دستی ایجاد کنید، اما اغلب تولید آن با استفاده از رنگهای منبع از برند شما آسانتر است. ابزار Material Theme Builder به شما این امکان را میدهد و به صورت اختیاری کد قالببندی Compose را صادر کنید.
شما میتوانید هر رنگی را که دوست دارید انتخاب کنید، اما برای مورد ما از رنگ اصلی پیشفرض Reply #825500 استفاده خواهید کرد. در بخش رنگهای اصلی سمت چپ، روی رنگ اصلی کلیک کنید و کد را در انتخابگر رنگ اضافه کنید.

اضافه کردن کد رنگ اصلی در Material Theme Builder.
وقتی رنگ اصلی را در سازنده تم متریال اضافه کردید، باید تم زیر و گزینه خروجی گرفتن را در گوشه بالا سمت راست ببینید. برای این codelab، تم را در Jetpack Compose خروجی میگیرید.

سازنده تم متریال با امکان خروجی گرفتن در گوشه بالا سمت راست.
رنگ اصلی #825500 تم زیر را ایجاد میکند که به برنامه اضافه خواهید کرد. متریال ۳ طیف گستردهای از نقشهای رنگی را برای بیان انعطافپذیر حالت، برجستگی و تأکید یک جزء ارائه میدهد.

طرح رنگ روشن و تیره از رنگ اصلی صادر شد.
فایل تولید شده The Color.kt شامل رنگهای تم شما به همراه تمام نقشهای تعریف شده برای رنگهای تم روشن و تیره است.
رنگ.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 است.
شما MaterialTheme() composable را در تابع 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() ، در بر بگیرید.
فعالیت اصلی.kt
setContent {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
AppTheme {
ReplyApp(/*..*/)
}
}
همچنین توابع پیشنمایش را بهروزرسانی خواهید کرد تا تم اعمالشده روی پیشنمایشهای برنامه را مشاهده کنید. برای اعمال تم روی پیشنمایشها ReplyApp composable را درون ReplyAppPreview() به همراه AppTheme قرار دهید.
شما هر دو تم سیستم روشن و تیره را در پارامترهای پیشنمایش تعریف کردهاید، بنابراین هر دو پیشنمایش را مشاهده خواهید کرد.
فعالیت اصلی.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
)
)
}
}
اگر اکنون برنامه را اجرا کنید، باید پیشنمایشهای برنامه را با رنگهای تم وارد شده به جای تم پایه مشاهده کنید.


برنامه با تم پایه (چپ).
برنامه با تم رنگی وارد شده (راست).

پیشنمایشهای برنامه روشن و تاریک با تمهای رنگی وارد شده.
متریال ۳ از هر دو طرح رنگی روشن و تیره پشتیبانی میکند. شما فقط برنامه را با قالب وارد شده بستهبندی کردهاید؛ اجزای متریال ۳ از نقشهای رنگی پیشفرض استفاده میکنند.
بیایید قبل از شروع افزودن رنگ به برنامه، در مورد نقشها و کاربردهای رنگها اطلاعات کسب کنیم.
نقشهای رنگی و دسترسیپذیری
هر نقش رنگی بسته به حالت، برجستگی و تأکید آن جزء، میتواند در مکانهای متنوعی مورد استفاده قرار گیرد.

نقشهای رنگی رنگهای اولیه، ثانویه و ثالثیه.
رنگ اصلی ، رنگ پایه است که برای اجزای اصلی مانند دکمههای برجسته و حالتهای فعال استفاده میشود.
رنگ کلید ثانویه برای اجزای کماهمیتتر در رابط کاربری، مانند تراشههای فیلتر، استفاده میشود.
رنگ کلید سوم برای ایجاد کنتراست و رنگهای خنثی برای پسزمینه و سطوح موجود در برنامه استفاده شده است.
سیستم رنگ Material مقادیر و اندازهگیریهای استاندارد تُن را ارائه میدهد که میتوانند برای دستیابی به نسبتهای کنتراست قابل دسترس مورد استفاده قرار گیرند. از on-primary روی primary، on-primary-container روی primary-container و همین کار را برای سایر رنگهای تأکیدی و خنثی انجام دهید تا کنتراست قابل دسترس برای کاربر فراهم شود.
برای اطلاعات بیشتر، به نقشهای رنگ و دسترسیپذیری مراجعه کنید.
ارتفاعات تُن و سایه
متریال ۳ عمدتاً با استفاده از پوششهای رنگی تُنال، ارتفاع را نشان میدهد. این یک روش جدید برای متمایز کردن ظروف و سطوح از یکدیگر است - افزایش تُنال تُنال علاوه بر سایهها، از تُن برجستهتری نیز استفاده میکند.
ارتفاع تُن در سطح ۲ که رنگ را از شکاف رنگ اصلی میگیرد.
پوششهای ارتفاعی در تمهای تیره نیز در طراحی متریال ۳ به پوششهای رنگی تغییر یافتهاند. رنگ پوشش از اسلات رنگ اصلی میآید.
M3 Surface - که پشت اکثر اجزای M3 قرار دارد - از هر دو قابلیت تنظیم تُن و سایه پشتیبانی میکند:
Surface(
modifier = modifier,
tonalElevation = {..}
shadowElevation = {..}
) {
Column(content = content)
}
اضافه کردن رنگ به برنامه
اگر برنامه را اجرا کنید، میتوانید رنگهای خروجی گرفته شده را در برنامهای که کامپوننتها رنگهای پیشفرض را میگیرند، مشاهده کنید. اکنون که از نقشها و کاربرد رنگها آگاه هستیم، بیایید تم برنامه را با نقشهای رنگی صحیح تنظیم کنیم.

برنامهای با تم رنگی و کامپوننتهایی که نقشهای رنگی پیشفرض را میگیرند.
رنگهای سطحی
در صفحه اصلی، شما با قرار دادن کامپوننت برنامه اصلی در یک Surface() شروع خواهید کرد تا پایهای برای قرار دادن محتوای برنامه روی آن فراهم شود. MainActivity.kt را باز کنید و کامپوننت ReplyApp() را با Surface قرار دهید.
همچنین میتوانید یک ارتفاع تُن (tonal elevation) برابر با 5.dp تعیین کنید تا به سطح، رنگ تُنی اسلات اصلی (primary slot) بدهید که به ایجاد کنتراست در برابر آیتم لیست و نوار جستجوی بالای آن کمک میکند. به طور پیشفرض، ارتفاع تُن و سایه برای سطح 0.dp است.
فعالیت اصلی.kt
AppTheme {
Surface(tonalElevation = 5.dp) {
ReplyApp(
replyHomeUIState = uiState,
// other parameters
)
}
}
اگر اکنون برنامه خود را اجرا کنید و هر دو صفحه List و Detail را ببینید، باید سطح رنگی اعمال شده بر کل برنامه را مشاهده کنید.


پسزمینه برنامه بدون سطح و رنگهای زمینه (چپ).
پسزمینه برنامه با رنگهای سطحی و تُنی اعمال شده (راست).
رنگهای نوار برنامه
نوار جستجوی سفارشی ما در بالا، برخلاف درخواست طراحی، پسزمینهی شفافی ندارد. بهطور پیشفرض، به سطح پایهی پیشفرض برمیگردد. میتوانید برای ایجاد یک تفکیک واضح، پسزمینهای برای آن در نظر بگیرید.

نوار جستجوی سفارشی بدون پسزمینه (چپ).
نوار جستجوی سفارشی با پسزمینه (سمت راست).
اکنون ui/components/ReplyAppBars.kt را که شامل نوار برنامه است، ویرایش خواهید کرد. MaterialTheme.colorScheme.background به Modifier Row Composable اضافه خواهید کرد.
پاسخAppBars.kt
@Composable
fun ReplySearchBar(modifier: Modifier = Modifier) {
Row(
modifier = modifier
.fillMaxWidth()
.padding(16.dp)
.background(MaterialTheme.colorScheme.background),
verticalAlignment = Alignment.CenterVertically
) {
// Search bar content
}
}
اکنون باید جدایی واضحی بین سطح تُنها و نوار برنامه با رنگ پسزمینه مشاهده کنید.

نوار جستجو با رنگ پس زمینه در بالای سطح تُنال.
رنگهای دکمههای عملیاتی شناور

FAB بزرگ بدون هیچ گونه قالببندی اعمال شده (چپ).
FAB بزرگ با تم رنگی سوم (راست).
در صفحه اصلی، میتوانید ظاهر دکمه عملیاتی شناور (FAB) را بهبود بخشید تا به عنوان یک دکمه فراخوان عمل (Call to action) برجسته شود. برای پیادهسازی این کار، یک رنگ تأکیدی سوم به آن اعمال خواهید کرد.
در فایل ReplyListContent.kt ، رنگ containerColor مربوط به FAB را به رنگ tertiaryContainer و رنگ محتوا را به onTertiaryContainer بهروزرسانی کنید تا دسترسیپذیری و تضاد رنگ حفظ شود.
پاسخListContent.kt
ReplyInboxScreen(/*..*/) {
// Email list content
LargeFloatingActionButton(
containerColor = MaterialTheme.colorScheme.tertiaryContainer,
contentColor = MaterialTheme.colorScheme.onTertiaryContainer
){
/*..*/
}
}
برنامه را اجرا کنید تا تم FAB خود را ببینید. برای این آزمایشگاه کد، شما از LargeFloatingActionButton استفاده میکنید.
رنگهای کارت
لیست ایمیل در صفحه اصلی از یک کامپوننت کارت استفاده میکند. به طور پیشفرض، این یک کارت Filled است که از رنگ متغیر سطح برای رنگ محفظه استفاده میکند تا جدایی واضحی بین رنگ سطح و رنگ کارت ایجاد کند. Compose همچنین پیادهسازیهایی از ElevatedCard و OutlinedCard را ارائه میدهد.
شما میتوانید با ارائه تُنهای رنگ ثانویه، برخی از موارد مهم را برجستهتر کنید. شما ui/components/ReplyEmailListItem.kt را با بهروزرسانی رنگ محفظه کارت با استفاده از CardDefaults.cardColors() برای ایمیلهای مهم، تغییر خواهید داد:
پاسخEmailListItem.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
)
){
/*..*/
}


با استفاده از رنگ ثانویه ظرف روی سطح تُنال، مورد لیست را هایلایت کنید.
رنگ آیتم لیست جزئیات
حالا که صفحه اصلی خود را تمگذاری کردهاید، با کلیک روی هر یک از موارد لیست ایمیل، نگاهی به صفحه جزئیات بیندازید.


صفحه جزئیات پیشفرض بدون آیتم فهرست موضوعی (چپ).
آیتم لیست جزئیات با تم پسزمینه اعمال شده (راست).
هیچ رنگی به آیتم لیست شما اعمال نشده است، از این رو به رنگ سطح با تُن رنگی پیشفرض برمیگردید. برای ایجاد جداسازی، رنگ پسزمینه را به آیتم لیست اعمال خواهید کرد و برای ایجاد فاصله در اطراف پسزمینه، فاصلهگذاری (padding) را اضافه خواهید کرد.
پاسخبهایمیلThreadItem.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
}
}
میتوانید ببینید که تنها با ارائه پسزمینه، جدایی واضحی بین سطح تُنال و مورد فهرست وجود دارد.
اکنون شما هم صفحات اصلی و هم صفحات جزئیات را با نقشها و کاربردهای صحیح رنگ دارید. بیایید ببینیم که برنامه شما چگونه میتواند از رنگهای پویا برای ارائه یک تجربه شخصیتر و منسجمتر استفاده کند.
۵. افزودن رنگهای پویا در برنامه
رنگ پویا بخش کلیدی Material 3 است که در آن یک الگوریتم، رنگهای سفارشی را از تصویر زمینه کاربر استخراج میکند تا در برنامهها و رابط کاربری سیستم او اعمال شود.
تمبندی پویا، برنامههای شما را شخصیسازیشدهتر میکند. همچنین تجربهای منسجم و یکپارچه با تم سیستم را برای کاربران فراهم میکند.
رنگ پویا در اندروید ۱۲ و بالاتر در دسترس است. اگر رنگ پویا در دسترس باشد، میتوانید با استفاده از 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
)
}

تم پویا برگرفته از والپیپر اندروید ۱۳.
حالا وقتی برنامه را اجرا میکنید، باید تم پویا را ببینید که با استفاده از تصویر زمینه پیشفرض اندروید ۱۳ اعمال شده است.
همچنین ممکن است بخواهید نوار وضعیت بسته به طرح رنگی که برای تم برنامه خود استفاده میکنید، به صورت پویا استایلبندی شود.

برنامه بدون اعمال رنگ نوار وضعیت (چپ).
برنامه با رنگ نوار وضعیت اعمال شده (راست).
برای بهروزرسانی رنگ نوار وضعیت بسته به رنگ اصلی قالب خود، رنگ نوار وضعیت را پس از انتخاب طرح رنگ در کامپوننت AppTheme اضافه کنید:
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
)
}
وقتی برنامه را اجرا میکنید، باید نوار وضعیت را با تم رنگی اصلی خود ببینید. همچنین میتوانید با تغییر تم تیره سیستم، تم پویای روشن و تیره را امتحان کنید.

تم پویای روشن (چپ) و تیره (راست) با تصویر زمینه پیشفرض اندروید ۱۳ اعمال شده است.
تا اینجا، شما رنگهایی را به برنامه خود اعمال کردهاید که ظاهر برنامه را بهبود بخشیده است. با این حال، میتوانید ببینید که تمام متنهای موجود در برنامه اندازه یکسانی دارند، بنابراین اکنون میتوانید تایپوگرافی را به برنامه اضافه کنید.
۶. تایپوگرافی
طراحی متریال ۳ یک مقیاس تایپ تعریف میکند. نامگذاری و گروهبندی به صورت زیر ساده شدهاند: نمایش، تیتر، عنوان، بدنه و برچسب، با اندازههای بزرگ، متوسط و کوچک برای هر کدام.

مقیاس نوع ۳ ماده.
تعریف تایپوگرافی
Compose کلاس M3 Typography را - به همراه کلاسهای TextStyle و font-related موجود - برای مدلسازی مقیاس نوع Material 3 ارائه میدهد.
سازندهی Typography برای هر سبک، مقادیر پیشفرض ارائه میدهد، بنابراین میتوانید هر پارامتری را که نمیخواهید سفارشیسازی کنید، حذف کنید. برای اطلاعات بیشتر، به بخش سبکهای تایپوگرافی و مقادیر پیشفرض آنها مراجعه کنید.
شما از پنج سبک تایپوگرافی در برنامه خود استفاده خواهید کرد: headlineSmall ، titleLarge ، bodyLarge ، bodyMedium و labelMedium . این سبکها هم صفحه اصلی و هم صفحه جزئیات را پوشش میدهند.

صفحه نمایش، تایپوگرافی استفاده شده در عنوان، برچسب و سبک بدنه را نشان میدهد.
سپس، به پکیج ui/theme بروید و Type.kt را باز کنید. کد زیر را اضافه کنید تا پیادهسازی خودتان را برای برخی از استایلهای متن به جای مقادیر پیشفرض ارائه دهید:
نوع.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
)
)
تایپوگرافی شما اکنون تعریف شده است. برای اضافه کردن آن به قالب خود، آن را به Composable از نوع MaterialTheme() در 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.k t استفاده کنید.
Text(
text = "Hello M3 theming",
style = MaterialTheme.typography.titleLarge
)
Text(
text = "you are learning typography",
style = MaterialTheme.typography.bodyMedium
)
احتمالاً محصول شما به هر ۱۵ سبک پیشفرض از مقیاس نوع طراحی متریال نیاز نخواهد داشت. در این آزمایشگاه کد، پنج اندازه انتخاب شده و بقیه حذف شدهاند.
از آنجایی که شما تایپوگرافی را به ترکیبهای Text( ) اعمال نکردهاید، تمام متن به طور پیشفرض به Typography.bodyLarge برمیگردد.
تایپوگرافی فهرست خانه
در مرحله بعد، تایپوگرافی را به تابع ReplyEmailListItem در ui/components/ReplyEmailListItem.kt اعمال کنید تا بین عناوین و برچسبها تمایز ایجاد شود:
پاسخEmailListItem.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
)


صفحه اصلی بدون تایپوگرافی اعمال شده (چپ).
صفحه اصلی با تایپوگرافی اعمال شده (راست).
تایپوگرافی لیست جزئیات
به طور مشابه، با بهروزرسانی تمام ترکیبهای متنی ReplyEmailThreadItem در ui i/components/ReplyEmailThreadItem.kt تایپوگرافی را در صفحه جزئیات اضافه خواهید کرد:
پاسخبهایمیلThreadItem.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
)


صفحه نمایش جزئیات بدون تایپوگرافی اعمال شده (چپ).
صفحه جزئیات با تایپوگرافی اعمال شده (راست).
سفارشیسازی تایپوگرافی
با استفاده از Compose، سفارشیسازی سبک متن یا ارائه فونت دلخواه بسیار آسان است. میتوانید TextStyle برای سفارشیسازی نوع فونت، خانواده فونت، فاصله حروف و غیره تغییر دهید.
شما سبک متن را در فایل theme/Type.kt تغییر خواهید داد، که در تمام کامپوننتهایی که از آن استفاده میکنند، اعمال خواهد شد.
برای titleLarge که برای موضوع در آیتم لیست استفاده میشود، fontWeight به SemiBold و lineHeight را به 32.sp بهروزرسانی کنید. این کار تأکید بیشتری بر موضوع ایجاد میکند و جداسازیهای واضحی را فراهم میکند.
نوع.kt
...
titleLarge = TextStyle(
fontWeight = FontWeight.SemiBold,
fontSize = 18.sp,
lineHeight = 32.sp,
letterSpacing = 0.0.sp
),
...

اعمال تایپوگرافی سفارشی به متن موضوع.
۷. اشکال
سطوح مواد را میتوان به شکلهای مختلفی نمایش داد. شکلها توجه را جلب میکنند، اجزا را شناسایی میکنند، حالت را منتقل میکنند و برند را بیان میکنند.
تعریف اشکال
Compose پارامترهای توسعهیافتهای را برای پیادهسازی شکلهای جدید M3 در اختیار کلاس Shapes قرار میدهد. مقیاس شکل M3، مشابه مقیاس نوع ، طیف وسیعی از اشکال گویا را در سراسر رابط کاربری امکانپذیر میکند.
در مقیاس شکل، اندازههای مختلفی از شکلها وجود دارد:
- خیلی کوچک
- کوچک
- متوسط
- بزرگ
- خیلی بزرگ
به طور پیشفرض، هر شکل یک مقدار پیشفرض دارد که میتواند لغو شود. برای برنامه شما، از شکل متوسط برای تغییر آیتم لیست استفاده خواهید کرد، اما میتوانید شکلهای دیگری را نیز تعریف کنید. یک فایل جدید به نام Shape.kt در بسته ui/theme ایجاد کنید و کد مربوط به شکلها را اضافه کنید:
شکل.کت
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 را تعریف کردهاید، آن را مانند کاری که برای رنگها و تایپوگرافی انجام دادید، به M3 MaterialTheme منتقل کنید:
Theme.kt
@Composable
fun AppTheme(
useDarkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable() () -> Unit
) {
// dynamic theming content
MaterialTheme(
colorScheme = colors,
typography = typography,
shapes = shapes
content = content
)
}
کار با اشکال
درست مانند رنگ و تایپوگرافی، میتوانید با استفاده از MaterialTheme.shape شکلها را به کامپوننتهای Material اعمال کنید، که به شما نمونه Shape برای دسترسی به شکلهای Material میدهد.
بسیاری از کامپوننتهای متریال از قبل شکلهای پیشفرضی دارند که روی آنها اعمال شده است، اما میتوانید شکلهای خودتان را از طریق اسلاتهای موجود ارائه داده و روی کامپوننتها اعمال کنید.
Card(shape = MaterialTheme.shapes.medium) { /* card content */ }
FloatingActionButton(shape = MaterialTheme.shapes.large) { /* fab content */}
نقشهبرداری از اجزای مواد با استفاده از انواع مختلف شکلها.
میتوانید نگاشت شکلها را برای همه اجزا در مستندات Shape مشاهده کنید.
دو شکل دیگر برای استفاده وجود دارد - RectangleShape و CircleShape - که بخشی از Compose هستند. شکل مستطیل شعاع حاشیه ندارد و شکل دایره لبههای کاملاً دایرهای را نشان میدهد.
همچنین میتوانید با استفاده از Modifiers که شکل میپذیرند، مانند Modifier.clip ، Modifier.background و Modifier.border ، به کامپوننتهای خود شکل اعمال کنید.
شکل نوار برنامه
ما میخواهیم نوار برنامه پسزمینهای با گوشههای گرد داشته باشد:

TopAppBar از یک Row با رنگ پسزمینه استفاده میکند. برای دستیابی به پسزمینه با گوشههای گرد، شکل پسزمینه را با ارسال CircleShape به اصلاحکننده پسزمینه تعریف کنید:
پاسخAppBars.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
}
}

شکل آیتم لیست جزئیات
در صفحه اصلی، شما از کارتی استفاده میکنید که به طور پیشفرض از Shape.Medium استفاده میکند. با این حال، برای صفحه جزئیات ما، به جای آن از یک ستون با رنگ پسزمینه استفاده کردید. برای ظاهر یکنواخت لیست، یک شکل متوسط به آن اعمال کنید.


ستون جزئیات اقلام لیست بدون شکل در سمت چپ و با شکل متوسط در سمت راست.
پاسخبهایمیلThreadItem.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 را به شما نشان میدهد.
۸. تأکید
تأکید در رابط کاربری به شما کمک میکند تا برخی از محتوا را نسبت به سایر محتواها برجسته کنید، مانند زمانی که میخواهید عنوان را از زیرنویسها متمایز کنید. تأکید در M3 از انواع رنگ و ترکیبهای رنگی آن استفاده میکند. شما دو راه برای افزودن تأکید دارید:
- استفاده از رنگهای سطحی، انواع سطحی و پسزمینه در کنار رنگهای روی سطح و انواع سطحی از سیستم رنگی توسعهیافته M3.
برای مثال، میتوان از surface به همراه on-surface-variant و از surface-variant به همراه on-surface برای ایجاد سطوح مختلف تأکید استفاده کرد.
انواع سطوح را میتوان با رنگهای تأکیدی نیز استفاده کرد تا تأکید کمتری نسبت به رنگهای تأکیدی داشته باشند، اما همچنان قابل فهم باشند و از نسبت کنتراست پیروی کنند.

نقشهای رنگی سطح، پسزمینه و انواع سطح.
- استفاده از وزنهای فونت مختلف برای متن. همانطور که در بخش تایپوگرافی مشاهده کردید، میتوانید وزنهای سفارشی را برای مقیاس فونت خود تعیین کنید تا تأکیدهای متفاوتی ایجاد شود.
در مرحله بعد، ReplyEmailListItem.kt را بهروزرسانی کنید تا با استفاده از نوع سطحی، تفاوت تأکید را ارائه دهید. به طور پیشفرض، محتوای کارت بسته به پسزمینه، رنگ محتوای پیشفرض را میگیرد.
شما رنگ متن زمان و متن بدنه را به onSurfaceVariant بهروزرسانی خواهید کرد. این کار در مقایسه با onContainerColors که به طور پیشفرض برای متنهای موضوع و عنوان اعمال میشود، تأکید آن را کاهش میدهد.


زمان و متن اصلی با تأکید یکسان در مقایسه با موضوع و عنوان (چپ).
زمان و بدنه با تأکید کمتر در مقایسه با موضوع و عنوان (راست).
پاسخEmailListItem.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 را دارد.
۹. تبریک
تبریک! شما این آزمایشگاه کد را با موفقیت به پایان رساندید! شما با استفاده از رنگها، تایپوگرافی و شکلها به همراه رنگهای پویا، تمبندی متریال را با Compose پیادهسازی کردهاید تا تم برنامه خود را ایجاد کرده و یک تجربه شخصیسازیشده ارائه دهید.



پایان نتایج تمبندی با رنگهای پویا و تم رنگی اعمال شده.
قدم بعدی چیست؟
سایر آزمایشگاههای کد ما را در مسیر Compose بررسی کنید:
مطالعه بیشتر
- راهنمای قالببندی آهنگسازی
- قالببندی متریال برای نوشتن
برنامههای نمونه
- نمونه برنامه پاسخ با تم کامل متریال ۳
- جتچت در حال نمایش قالببندی پویا