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

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

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

نقطة نهاية الدرس التطبيقي حول الترميز الخاص بتطبيق السمات مع سمات الألوان الداكنة والسمات الديناميكية الداكنة
المتطلبات
- أحدث إصدار من "استوديو Android"
- خبرة أساسية في لغة Kotlin
- فهم أساسي لـ Jetpack Compose
- معرفة أساسية بتنسيقات Compose، مثل Row و Column و Modifier
2. الإعداد
في هذه الخطوة، ستنزّل الرمز الكامل لتطبيق Reply الذي ستصمّمه في هذا الدرس التطبيقي حول الترميز.
الحصول على الشفرة
يمكنك العثور على الرمز البرمجي لهذا الدرس التطبيقي حول الترميز في مستودع android-compose-codelabs على GitHub. لنسخ المستودع، نفِّذ الأمر التالي:
$ git clone https://github.com/googlecodelabs/android-compose-codelabs
يمكنك بدلاً من ذلك تنزيل ملفَي zip:
تجربة نموذج تطبيق
يحتوي الرمز الذي نزّلته للتو على رمز لجميع دروس Compose البرمجية المتاحة. لإكمال هذا الدرس العملي، افتح مشروع ThemingCodelab داخل "استوديو Android".
ننصحك بالبدء بالرمز البرمجي في الفرع الرئيسي واتّباع الخطوات الواردة في الدرس التطبيقي حول الترميز خطوة بخطوة بالسرعة التي تناسبك. يمكنك في أي وقت تشغيل أي من الإصدارين في "استوديو Android" من خلال تغيير فرع git للمشروع.
استكشاف رمز البدء
يحتوي الرمز البرمجي الرئيسي على حزمة واجهة مستخدم تتضمّن الحِزم والملفات الرئيسية التالية التي ستتفاعل معها:
-
MainActivity.kt: نشاط نقطة الدخول الذي تبدأ منه تطبيق "الرد الذكي". 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 مخصّص يدويًا، غالبًا ما يكون من الأسهل إنشاء باستخدام ألوان المصدر من علامتك التجارية. تتيح لك أداة Material Theme Builder إجراء ذلك، كما تتيح لك اختياريًا تصدير رمز تصميم Compose.
يمكنك اختيار أي لون تريده، ولكن في حالة الاستخدام هذه، ستستخدم لون الردّ الأساسي التلقائي #825500. انقر على اللون الأساسي في قسم الألوان الأساسية على اليمين وأضِف الرمز في أداة اختيار الألوان.

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

أدوار الألوان الخاصة بالسطح والخلفية والسطح البديل
- استخدام أوزان خطوط مختلفة للنص كما رأيت في قسم الطباعة، يمكنك توفير أوزان مخصّصة لمقياس الكتابة من أجل توفير مستويات مختلفة من التركيز.
بعد ذلك، عدِّل 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 التعليمي:
محتوى إضافي للقراءة
تطبيقات نموذجية
- تطبيق Reply sample مع تصميم كامل باستخدام Material 3
- JetChat يعرض ميزة تغيير المظهر بشكل ديناميكي