1. مقدمة
في هذا الدرس التطبيقي حول الترميز، ستتعرّف على كيفية تطبيق السمات على تطبيقاتك في Jetpack Compose باستخدام التصميم المتعدد الأبعاد 3. ستتعرّف أيضًا على اللبنات الأساسية لمخططات الألوان وأساليب الخطوط والأشكال في التصميم المتعدد الأبعاد 3، والتي تساعدك في تصميم تطبيقك بطرق مخصّصة ومتاحة للجميع.
بالإضافة إلى ذلك، ستتعرّف على إمكانية استخدام السمات الديناميكية ومستويات التركيز المختلفة.
ما ستتعلمه
في هذا الدرس العملي، ستتعرّف على ما يلي:
- الجوانب الرئيسية لتحديد نسق Material 3
- أنظمة ألوان Material 3 وطريقة إنشاء مظاهر لتطبيقك
- كيفية توفير توافق تطبيقك مع المظهر الديناميكي والمظهر الفاتح/الداكن
- الخطوط والأشكال لتخصيص تطبيقك
- مكوّنات Material 3 وتخصيصها لتصميم تطبيقك
ما ستنشئه
في هذا الدرس التطبيقي حول الترميز، ستنشئ مظهرًا لتطبيق برنامج بريد إلكتروني يُسمى Reply. ستبدأ بتطبيق غير منمّق باستخدام المظهر الأساسي، وستطبّق ما تعلّمته لتنميق التطبيق وتوفير مظاهر داكنة.

نقطة البداية التلقائية لتطبيقنا مع المظهر الأساسي
ستنشئ المظهر باستخدام نظام الألوان وأسلوب الخط والأشكال، ثم ستطبّقه على قائمة عناوين البريد الإلكتروني وصفحة التفاصيل في تطبيقك. ستضيف أيضًا إمكانية استخدام المظهر الديناميكي في التطبيق. وبنهاية الدرس التطبيقي حول الترميز، ستتمكّن من استخدام كلّ من الألوان والمظاهر الديناميكية في تطبيقك.

نقطة نهاية الدرس التطبيقي حول الترميز الخاص بالنسق، مع نسق ألوان فاتحة ونسق ديناميكي فاتح

نقطة نهاية الدرس التطبيقي حول الترميز حول تصميم المظاهر باستخدام ميزة "تصميم المظاهر الديناميكية" والمظهر الداكن
المتطلبات
- أحدث إصدار من "استوديو Android"
- خبرة أساسية في استخدام لغة Kotlin
- فهم أساسي لـ Jetpack Compose
- معرفة أساسية بتنسيقات Compose، مثل Row و Column و Modifier
2. الإعداد
في هذه الخطوة، ستنزّل الرمز الكامل لتطبيق Reply الذي ستصمّمه في هذا الدرس التطبيقي حول الترميز.
الحصول على الشفرة
يمكنك العثور على الرمز البرمجي لهذا الدرس التطبيقي حول الترميز في مستودع GitHub android-compose-codelabs. لنسخها، نفِّذ الأمر التالي:
$ git clone https://github.com/googlecodelabs/android-compose-codelabs
يمكنك بدلاً من ذلك تنزيل ملفَي zip:
تجربة نموذج تطبيق
يحتوي الرمز الذي نزّلته للتو على رمز لجميع دروس Compose البرمجية المتاحة. لإكمال هذا الدرس العملي، افتح مشروع ThemingCodelab داخل "استوديو Android".
ننصحك بالبدء بالرمز البرمجي في الفرع الرئيسي واتّباع الخطوات الواردة في الدرس التطبيقي حول الترميز خطوة بخطوة بالسرعة التي تناسبك. يمكنك في أي وقت تشغيل أي من الإصدارين في "استوديو Android" من خلال تغيير فرع git للمشروع.
استكشاف رمز البدء
يحتوي الرمز البرمجي الرئيسي على حزمة واجهة مستخدم تتضمّن الحِزم والملفات الرئيسية التالية التي ستتفاعل معها:
-
MainActivity.kt: نشاط نقطة الدخول الذي تبدأ منه تطبيق Reply. com.example.reply.ui.theme: تحتوي هذه الحزمة على المظاهر وأسلوب الخط وأنظمة الألوان. ستضيف في هذه الحزمة إمكانية "تخصيص التصميم المتعدّد الأبعاد".com.example.reply.ui.components: يحتوي على المكوّنات المخصّصة للتطبيق، مثل عناصر القائمة وأشرطة التطبيق وما إلى ذلك. سيتم تطبيق النُسق على هذه المكوّنات.-
ReplyApp.kt: هذه هي الدالة المركّبة الرئيسية التي ستبدأ منها شجرة واجهة المستخدم. ستطبّق ميزة "تحديد السمات على مستوى أعلى" في هذا الملف.
سيركّز هذا الدرس التطبيقي حول الترميز على ملفات حزمة ui.
3- تحديد نسق Material 3
توفّر Jetpack Compose تنفيذًا للتصميم المتعدد الأبعاد، وهو نظام تصميم شامل لإنشاء واجهات رقمية. تم إنشاء المكوّنات في التصميم المتعدد الأبعاد (مثل الأزرار والبطاقات ومفاتيح التبديل وما إلى ذلك) استنادًا إلى تخصيص التصميم المتعدد الأبعاد، وهي طريقة منهجية لتخصيص التصميم المتعدد الأبعاد من أجل عرض العلامة التجارية لمنتجك بشكل أفضل.
يتضمّن مظهر Material 3 الأنظمة الفرعية التالية لإضافة مظهر إلى تطبيقك: نظام الألوان وأسلوب الخط والأشكال. عند تخصيص هذه القيم، تنعكس التغييرات تلقائيًا في مكوّنات M3 التي تستخدمها لإنشاء تطبيقك. لنستكشف كل نظام فرعي وننفّذه في نموذج التطبيق.

نظام فرعي من Material 3 للألوان والطباعة والأشكال
4. الألوان
أساس نظام الألوان هو مجموعة من خمسة ألوان رئيسية يرتبط كل منها بلوحة ألوان مكوّنة من 13 درجة لونية تستخدمها مكوّنات Material 3.

خمسة ألوان أساسية لإنشاء تصميم M3
يتم بعد ذلك توفير كل لون تمييز (أساسي وثانوي وثالث) بأربعة ألوان متوافقة بدرجات مختلفة لتسهيل عملية الدمج وتحديد مستوى التركيز والتعبير المرئي.

أربعة ألوان نغمية لألوان التمييز الأساسية والثانوية والثالثية.
وبالمثل، تنقسم الألوان المحايدة أيضًا إلى أربع درجات متوافقة تُستخدَم للأسطح والخلفية. من المهم أيضًا استخدامها لإبراز رموز النصوص عند وضعها على أي سطح.

أربعة ألوان متدرّجة من الألوان المحايدة الأساسية
مزيد من المعلومات حول نظام الألوان وأدوار الألوان
إنشاء أنظمة الألوان
على الرغم من أنّه يمكنك إنشاء ColorScheme مخصّص يدويًا، غالبًا ما يكون من الأسهل إنشاء ColorScheme باستخدام ألوان المصدر من علامتك التجارية. تتيح لك أداة Material Theme Builder إجراء ذلك، كما تتيح لك اختياريًا تصدير رمز تصميم Compose.
يمكنك اختيار أي لون تريده، ولكن في حالة الاستخدام هذه، ستستخدم لون الردّ الأساسي التلقائي #825500. انقر على اللون الأساسي في قسم الألوان الأساسية على اليمين وأضِف الرمز في أداة اختيار الألوان.

إضافة رمز اللون الأساسي في أداة إنشاء "مظهر Material"
بعد إضافة اللون الأساسي في أداة Material Theme Builder، من المفترض أن يظهر لك المظهر التالي وخيار التصدير في أعلى يسار الصفحة. في هذا الدرس التطبيقي، ستصدّر السمة في Jetpack Compose.

أداة إنشاء "مظهر 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.
عليك تضمين الدالة القابلة للإنشاء 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
)
)
}
}
إذا شغّلت التطبيق الآن، من المفترض أن تظهر لك معاينات التطبيق بألوان المظهر التي تم استيرادها بدلاً من المظهر الأساسي.

تطبيق ذو مظهر أساسي (على اليمين)
تطبيق يتضمّن نسق ألوان مستوردًا (على اليسار)

معاينات التطبيقات بالمظهرين الفاتح والداكن مع أنظمة الألوان المستوردة
يتوافق Material 3 مع أنظمة الألوان الفاتحة والداكنة. لقد غلّفت التطبيق بالمظهر المستورد فقط، وتستخدم مكوّنات Material 3 أدوار الألوان التلقائية.
لنتعرّف على أدوار الألوان واستخدامها قبل البدء في إضافتها إلى التطبيق.
أدوار الألوان وإمكانية الوصول
يمكن استخدام كل دور من أدوار الألوان في مجموعة متنوعة من الأماكن حسب حالة المكوّن وبروزه وأهميته.

أدوار الألوان الأساسية والثانوية والثالثة
اللون الأساسي هو اللون الأساسي الذي يُستخدَم في المكوّنات الرئيسية، مثل الأزرار البارزة والحالات النشطة.
يُستخدم لون المفتاح الثانوي للمكوّنات الأقل بروزًا في واجهة المستخدم، مثل شرائح الفلاتر.
يُستخدم لون المفتاح الثالث لتوفير لمسات متباينة، وتُستخدم الألوان المحايدة للخلفية والأسطح في التطبيق.
يوفر نظام الألوان في Material قيم درجات الألوان وقياسات عادية يمكن استخدامها لاستيفاء نسب التباين المناسبة. استخدِم on-primary فوق primary، وon-primary-container فوق primary-container، وينطبق الأمر نفسه على ألوان التمييز والألوان المحايدة الأخرى لتوفير تباين يسهل الوصول إليه للمستخدم.
لمزيد من المعلومات، يُرجى الاطّلاع على أدوار الألوان وتسهيل الاستخدام.
الارتفاعات اللونية والظلّية
تمثّل Material 3 مستوى الارتفاع بشكل أساسي باستخدام تراكبات الألوان الدرجات اللونية. هذه طريقة جديدة للتمييز بين الحاويات والمساحات، إذ إنّ زيادة الارتفاع النغمي تستخدم درجة لون أكثر بروزًا، بالإضافة إلى الظلال.
الارتفاع المتدرّج في المستوى 2 الذي يأخذ اللون من خانة اللون الأساسي
تم أيضًا تغيير تراكبات المسقط الرأسي في المظاهر الداكنة إلى تراكبات الألوان النغمية في التصميم المتعدد الأبعاد 3. يأتي لون التراكب من خانة اللون الأساسي.
يتضمّن Surface M3، وهو العنصر القابل للإنشاء في الخلفية لمعظم مكوّنات M3، إمكانية استخدام كلّ من الارتفاع اللوني وارتفاع الظل:
Surface(
modifier = modifier,
tonalElevation = {..}
shadowElevation = {..}
) {
Column(content = content)
}
إضافة ألوان إلى التطبيق
إذا شغّلت التطبيق، يمكنك الاطّلاع على الألوان التي تم تصديرها وهي تظهر في التطبيق حيث تستخدم المكوّنات الألوان التلقائية. بعد أن تعرّفنا على أدوار الألوان واستخداماتها، لنطبّق الآن المظهر على التطبيق باستخدام أدوار الألوان الصحيحة.

تطبيق يتضمّن نسق الألوان ومكوّنات تتولّى أدوار الألوان التلقائية
ألوان السطح
في الشاشة الرئيسية، ستبدأ بتضمين العنصر القابل للإنشاء الرئيسي للتطبيق في Surface() لتوفير الأساس الذي سيتم وضع محتوى التطبيق فوقه. افتح MainActivity.kt وغلِّف العنصر القابل للإنشاء ReplyApp() باستخدام Surface.
ستوفّر أيضًا ارتفاعًا صوتيًا بمقدار 5.dp لمنح السطح لونًا صوتيًا للخانة الأساسية، ما يساعد في توفير التباين مع عنصر القائمة وشريط البحث في أعلى السطح. يكون مستوى الارتفاع التلقائي للسطح 0.dp.
MainActivity.kt
AppTheme {
Surface(tonalElevation = 5.dp) {
ReplyApp(
replyHomeUIState = uiState,
// other parameters
)
}
}
إذا شغّلت تطبيقك الآن وظهرت لك كلّ من صفحة "القائمة" و"التفاصيل"، من المفترض أن يظهر السطح اللوني مطبَّقًا على التطبيق بأكمله.

خلفية التطبيق بدون لون سطحي ولون نغمي (على اليمين)
خلفية التطبيق مع تطبيق لون السطح واللون الدرجتي (على اليسار)
ألوان شريط التطبيق
لا يحتوي شريط البحث المخصّص في أعلى الصفحة على خلفية شفافة كما هو مطلوب في التصميم. ويتم تلقائيًا الرجوع إلى السطح الأساسي التلقائي. يمكنك توفير خلفية لتقديم فصل واضح.

شريط بحث مخصّص بدون خلفية (على اليمين)
شريط بحث مخصّص مع خلفية (على اليسار)
عدِّل الآن 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
}
}
من المفترض أن ترى الآن فصلاً واضحًا بين السطح اللوني وشريط التطبيقات مع لون الخلفية.

شريط بحث بلون خلفية فوق سطح لوني
ألوان زر الإجراء الرئيسي

زر الإجراء الرئيسي (FAB) كبير بدون تطبيق أي مظهر (على اليسار).
زر الإجراء العائم الكبير ذو الطابع الخاص مع لون ثانوي (على اليسار)
على الشاشة الرئيسية، يمكنك تحسين مظهر زر الإجراء الرئيسي (FAB) لكي يبرز كزر يحث المستخدم على اتّخاذ إجراء. لتنفيذ ذلك، عليك تطبيق لون تمييز ثانوي عليه.
في ملف ReplyListContent.kt، عدِّل containerColor الخاص بزر الإجراء الرئيسي (FAB) إلى اللون tertiaryContainer ولون المحتوى إلى onTertiaryContainer للحفاظ على تسهيل الاستخدام وتباين الألوان.
ReplyListContent.kt
ReplyInboxScreen(/*..*/) {
// Email list content
LargeFloatingActionButton(
containerColor = MaterialTheme.colorScheme.tertiaryContainer,
contentColor = MaterialTheme.colorScheme.onTertiaryContainer
){
/*..*/
}
}
شغِّل التطبيق للاطّلاع على الزرّ العائم ذي الإجراءات المخصّصة. في هذا الدرس التطبيقي، ستستخدم 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
)
){
/*..*/
}

تمييز عنصر القائمة باستخدام لون الحاوية الثانوية على سطح لوني
لون عنصر قائمة التفاصيل
لقد اخترت الآن مظهرًا لشاشتك الرئيسية. ألقِ نظرة على صفحة التفاصيل من خلال النقر على أي من عناصر قائمة عناوين البريد الإلكتروني.

صفحة التفاصيل التلقائية بدون عنصر قائمة ذي مظهر (على اليمين)
عنصر قائمة التفاصيل مع تطبيق تصميم الخلفية (على اليسار)
لم يتم تطبيق أي لون على عنصر القائمة، لذا يتم الرجوع إلى لون السطح المتدرّج التلقائي. ستطبّق لون الخلفية على عنصر القائمة لإنشاء فاصل وتضيف مساحة متروكة لتوفير مساحة حول الخلفية.
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، حيث تستخلص خوارزمية ألوانًا مخصّصة من خلفية المستخدم لتطبيقها على تطبيقاته وواجهة مستخدم النظام.
تساهم ميزة "تغيير المظهر الديناميكي" في تخصيص تطبيقاتك بشكل أكبر. كما يوفّر للمستخدمين تجربة متماسكة وسلسة مع مظهر النظام.
تتوفّر ميزة "الألوان الديناميكية" على الإصدار 12 من نظام التشغيل Android والإصدارات الأحدث. إذا كانت ميزة "الألوان الديناميكية" متاحة، يمكنك إعداد نظام ألوان ديناميكي باستخدام 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
)
}

مظهر ديناميكي مأخوذ من خلفية Android 13
عند تشغيل التطبيق الآن، من المفترض أن يظهر لك المظهر الديناميكي الذي تم تطبيقه باستخدام خلفية Android 13 التلقائية.
قد تحتاج أيضًا إلى أن يتم تصميم شريط الحالة بشكل ديناميكي استنادًا إلى نظام الألوان المستخدَم في تصميم تطبيقك.

تطبيق بدون لون شريط الحالة (على اليمين)
تطبيق تم تطبيق لون شريط الحالة عليه (على اليسار)
لتعديل لون شريط الحالة استنادًا إلى اللون الأساسي للمظهر، أضِف لون شريط الحالة بعد اختيار نظام الألوان في الدالة البرمجية القابلة للإنشاء 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
)
}
عند تشغيل التطبيق، من المفترض أن يظهر شريط الحالة بلون السمة الأساسي. يمكنك أيضًا تجربة المظهر الديناميكي الفاتح والداكن من خلال تغيير المظهر الداكن للنظام.

المظهر الفاتح (على اليمين) والداكن (على اليسار) الديناميكيان مطبّقان مع خلفية Android 13 التلقائية.
حتى الآن، طبّقت ألوانًا على تطبيقك حسّنت من مظهره. ومع ذلك، يمكنك ملاحظة أنّ جميع النصوص في التطبيق لها الحجم نفسه، لذا يمكنك الآن إضافة طباعة إلى التطبيق.
6. فن الطباعة
يحدّد الإصدار 3 من التصميم المتعدد الأبعاد مقياسًا للخطوط. تم تبسيط التسمية والتجميع إلى: إعلان صوري وعنوان رئيسي وعنوان ونص أساسي وتصنيف، مع أحجام كبيرة ومتوسطة وصغيرة لكل منها.

مقياس النوع Material 3
تحديد أسلوب الخط
يوفر Compose الفئة Typography الخاصة بتصميم Material 3، بالإضافة إلى الفئتين الحالتين TextStyle وfont-related، لنمذجة مقياس نوع Material 3.
يوفّر منشئ الطباعة إعدادات تلقائية لكل نمط، ما يتيح لك حذف أي مَعلمات لا تريد تخصيصها. لمزيد من المعلومات، يُرجى الاطّلاع على أنماط أسلوب الخط وقيمه التلقائية.
ستستخدم خمسة أنماط طباعة في تطبيقك: 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() داخل 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 من مقياس الكتابة في التصميم المتعدد الأبعاد. في هذا الدرس العملي، تم اختيار خمسة أحجام وتم حذف الباقي.
بما أنّك لم تطبّق أسلوب الخط على عناصر 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
)

الشاشة الرئيسية بدون تطبيق الطباعة (على اليمين)
الشاشة الرئيسية مع تطبيق الطباعة (على اليسار)
أسلوب الخط في قائمة التفاصيل
وبالمثل، ستضيف الطباعة في شاشة التفاصيل من خلال تعديل جميع عناصر النص القابلة للإنشاء في 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
)

شاشة التفاصيل بدون تطبيق أسلوب الخط (على اليمين)
شاشة التفاصيل مع تطبيق أسلوب الخط (على اليسار)
تخصيص أسلوب الخط
باستخدام Compose، يمكنك بسهولة تخصيص نمط النص أو توفير الخط المخصّص. يمكنك تعديل 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
),
...

تطبيق أسلوب خط مخصّص على نص الموضوع
7. الأشكال
يمكن عرض مساحات عرض Material بأشكال مختلفة. توجّه الأشكال الانتباه وتحدّد المكوّنات وتوضّح الحالة وتعرض العلامة التجارية.
تحديد الأشكال
توفر Compose الفئة 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 في Material 3 كما فعلت مع الألوان وأسلوب الخط:
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 باستخدام أنواع مختلفة من الأشكال
يمكنك الاطّلاع على عملية ربط الأشكال بجميع المكوّنات في المستندات الخاصة بالأشكال.
يتوفّر شكلان آخران يمكن استخدامهما، وهما RectangleShape وCircleShape، وهما جزء من ميزة "إنشاء". لا يحتوي شكل المستطيل على نصف قطر للحدود، بينما يعرض شكل الدائرة حوافًا دائرية كاملة.
يمكنك أيضًا تطبيق الشكل على "المكوّنات" باستخدام Modifiers التي تأخذ أشكالاً، مثل Modifier.clip وModifier.background وModifier.border.
شكل شريط التطبيق
نريد أن يحتوي شريط التطبيق على خلفية ذات زوايا مستديرة:

يستخدم 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
}
}

شكل عنصر قائمة التفاصيل
في الشاشة الرئيسية، أنت تستخدم بطاقة تستخدم Shape.Medium تلقائيًا. ومع ذلك، استخدمت في صفحة التفاصيل عمودًا بلون خلفية بدلاً من ذلك. للحصول على مظهر موحّد للقائمة، طبِّق عليها شكلاً متوسطًا.

عمود عنصر القائمة التفصيلية بدون شكل في عنصر القائمة (على اليمين) وشكل متوسط في القائمة (على اليسار)
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 درجات مختلفة من الألوان ومجموعات الألوان المتناسبة معها لإبراز العناصر. هناك طريقتان لإضافة التوكيد:
- استخدام ألوان "السطح" و"السطح المتغير" و"الخلفية" إلى جانب ألوان "على السطح" و"على السطح المتغير" من نظام الألوان الموسّع في Material 3
على سبيل المثال، يمكن استخدام السطح مع on-surface-variant، ويمكن استخدام surface-variant مع on-surface لتوفير مستويات مختلفة من التركيز.
يمكن أيضًا استخدام أشكال مختلفة من الخلفية مع ألوان مميّزة لإبرازها بشكل أقل من الألوان المميزة، ولكن مع الحفاظ على سهولة الاستخدام واتّباع نسبة التباين.

أدوار الألوان الخاصة بالسطح والخلفية والسطح المتغير
- استخدام أوزان خطوط مختلفة للنص كما رأيت في قسم الطباعة، يمكنك توفير أوزان مخصّصة لمقياس الكتابة من أجل توفير مستويات مختلفة من التركيز.
بعد ذلك، عدِّل ReplyEmailListItem.kt لتقديم اختلاف في التركيز باستخدام السمة المتغيرة للسطح. تلقائيًا، يظهر محتوى البطاقة بلون المحتوى التلقائي استنادًا إلى الخلفية.
عليك تعديل لون العنصرَين القابلَين للإنشاء الخاصَين بنص الوقت ونص الرسالة إلى onSurfaceVariant. يؤدي ذلك إلى تقليل أهميته مقارنةً بـ onContainerColors، والذي يتم تطبيقه على العناصر القابلة للإنشاء الخاصة بالنصوص والمواضيع والعناوين تلقائيًا.

الوقت ونص الرسالة مع التركيز نفسه مقارنةً بالموضوع والعنوان (على اليمين)
الوقت والنص مع التركيز بشكل أقل مقارنةً بالموضوع والعنوان (على اليسار)
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- تهانينا
تهانينا! لقد أكملت هذا الدرس التطبيقي حول الترميز بنجاح. لقد نفّذت "تخصيص التصميم المتعدّد الأبعاد" باستخدام Compose من خلال الألوان وأسلوب الخط والأشكال بالإضافة إلى الألوان الديناميكية لتصميم تطبيقك وتوفير تجربة مخصّصة.

نهاية نتائج تطبيق النسق مع الألوان الديناميكية ونسق الألوان
الخطوات التالية
اطّلِع على دروسنا التطبيقية الأخرى حول مسار Compose التعليمي:
محتوى إضافي للقراءة
- دليل مظهر Compose
- تخصيص التصميم المتعدد الأبعاد في Compose
تطبيقات نموذجية
- تطبيق Reply sample مع تصميم كامل باستخدام Material 3
- تطبيق JetChat يعرض ميزة تغيير المظهر بشكل ديناميكي