الدرس الأول: الدوال القابلة للإنشاء
تم تصميم Jetpack Compose استنادًا إلى دوال قابلة للإنشاء. وتتيح لك هذه الدوال إمكانية تحديد واجهة المستخدم الخاصة بتطبيقك بشكل آلي من خلال وصف شكلها وعرض تبعيات البيانات بدلاً من التركيز على عملية إنشاء واجهة المستخدم (إعداد عنصر أو إرفاقه بأحد العناصر الرئيسية، وما إلى ذلك). لإنشاء دالة قابلة للإنشاء، ما عليك سوى إضافة
التعليق التوضيحي @Composable
إلى اسم الدالة.
إضافة عنصر نصي
للبدء، نزِّل أحدث إصدار من استوديو Android وأنشِئ تطبيقًا من خلال النقر على مشروع جديد، ثم انقر على نشاط فارغ ضمن فئة الهاتف والجهاز اللوحي. أدخِل اسمًا لتطبيقك Composetutorial أو انقر على إنهاء. يحتوي النموذج التلقائي حاليًا على بعض عناصر Compose، ولكن في هذا البرنامج التعليمي، ستنشئه خطوة بخطوة.
أولاً، اعرض النص "Hello world!" من خلال إضافة عنصر نصي في
طريقة onCreate
. ويمكنك إجراء ذلك من خلال تحديد حظر محتوى واستدعاء الدالة
Text
القابلة للإنشاء. تحدِّد مجموعة setContent
تنسيق النشاط الذي يستدعي فيه الدوال القابلة للإنشاء. لا يمكن استدعاء الدوال القابلة للإنشاء إلا من دوال أخرى قابلة للإنشاء.
يستخدم Jetpack Compose مكوّنًا إضافيًا لبرنامج تجميع Kotlin لتحويل هذه الوظائف القابلة للإنشاء إلى عناصر واجهة المستخدم في التطبيق. على سبيل المثال، تعرض الدالة Text
القابلة للإنشاء، التي يتم تحديدها من خلال مكتبة واجهة المستخدم Compose، تصنيفًا نصيًا على الشاشة.
import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.material3.Text class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { Text("Hello world!") } } }
تعريف دالة قابلة للإنشاء
لجعل الدالة قابلة للإنشاء، أضِف التعليق التوضيحي @Composable
.
لتجربة ذلك، حدِّد دالة MessageCard
التي
يتم تمرير اسم لها، وتستخدمها لإعداد العنصر النصي.
// ... import androidx.compose.runtime.Composable class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { MessageCard("Android") } } } @Composable fun MessageCard(name: String) { Text(text = "Hello $name!") }
معاينة الدالة في "استوديو Android"
يتيح لك التعليق التوضيحي @Preview
معاينة الدوال القابلة للإنشاء ضمن "استوديو Android" بدون الحاجة إلى إنشاء التطبيق وتثبيته على جهاز Android أو محاكي. يجب استخدام التعليق التوضيحي على دالة قابلة للإنشاء لا تقبل المعلَمات. لهذا السبب، لا يمكنك معاينة الدالة MessageCard
مباشرةً. بدلاً من ذلك، أنشِئ دالة ثانية باسم
PreviewMessageCard
تستدعي MessageCard
مع معلَمة مناسبة. أضِف التعليق التوضيحي @Preview
قبل @Composable
.
// ... import androidx.compose.ui.tooling.preview.Preview @Composable fun MessageCard(name: String) { Text(text = "Hello $name!") } @Preview @Composable fun PreviewMessageCard() { MessageCard("Android") }
أعِد إنشاء مشروعك. لا يطرأ أي تغيير على التطبيق نفسه، لأنّ وظيفة
PreviewMessageCard
الجديدة لا تستدعي أي مكان،
إلا أنّ "استوديو Android" يضيف نافذة معاينة يمكنك توسيعها من خلال النقر على طريقة عرض التقسيم
(التصميم/الرمز). تعرض هذه النافذة معاينة لعناصر واجهة المستخدم التي تم إنشاؤها بواسطة دوال قابلة للإنشاء
تم وضع علامة عليها باستخدام التعليق التوضيحي @Preview
. لتعديل
المعاينات في أي وقت، انقر على زر إعادة التحميل في أعلى نافذة المعاينة.
import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.material3.Text class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { Text("Hello world!") } } }
// ... import androidx.compose.runtime.Composable class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { MessageCard("Android") } } } @Composable fun MessageCard(name: String) { Text(text = "Hello $name!") }
// ... import androidx.compose.ui.tooling.preview.Preview @Composable fun MessageCard(name: String) { Text(text = "Hello $name!") } @Preview @Composable fun PreviewMessageCard() { MessageCard("Android") }
الدرس الثاني: التخطيطات
عناصر واجهة المستخدم هرمية، مع عناصر مضمنة في عناصر أخرى. في Compose، يمكنك إنشاء تسلسل هرمي لواجهة المستخدم من خلال طلب دوال قابلة للإنشاء من دوال أخرى قابلة للإنشاء.
إضافة نصوص متعددة
لقد أنشأت حتى الآن أول دالة قابلة للإنشاء والمعاينة! لاكتشاف المزيد من إمكانات Jetpack Compose، عليك إنشاء شاشة مراسلة بسيطة تحتوي على قائمة بالرسائل التي يمكن توسيعها باستخدام بعض الصور المتحركة.
ابدأ بجعل الرسالة قابلة للإنشاء أكثر ثراءً من خلال عرض اسم مؤلفها
ومحتوى الرسالة. عليك أولاً تغيير المَعلمة القابلة للإنشاء لقبول عنصر
Message
بدلاً من
String
وإضافة
Text
آخر قابل للإنشاء في عنصر
MessageCard
القابل للإنشاء. احرص على تعديل المعاينة
أيضًا.
// ... class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { MessageCard(Message("Android", "Jetpack Compose")) } } } data class Message(val author: String, val body: String) @Composable fun MessageCard(msg: Message) { Text(text = msg.author) Text(text = msg.body) } @Preview @Composable fun PreviewMessageCard() { MessageCard( msg = Message("Lexi", "Hey, take a look at Jetpack Compose, it's great!") ) }
تُنشئ هذه التعليمة البرمجية عنصرين نصيين داخل عرض المحتوى. وبما أنّك لم تقدّم أي معلومات حول طريقة ترتيبها، يتم رسم عناصر النص فوق بعضها، ما يجعل النص غير قابل للقراءة.
استخدام عمود
// ... import androidx.compose.foundation.layout.Column @Composable fun MessageCard(msg: Message) { Column { Text(text = msg.author) Text(text = msg.body) } }
إضافة عنصر صورة
يمكنك تحسين بطاقة رسالتك من خلال إضافة صورة ملف شخصي للمُرسِل. استخدِم مدير الموارد لاستيراد صورة من مكتبة الصور أو استخدِم هذه الصورة. أضِف عنصر
Row
قابل للإنشاء للحصول على تصميم منظّم بشكل جيد
و
Image
قابل للإنشاء بداخله.
// ... import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Row import androidx.compose.ui.res.painterResource @Composable fun MessageCard(msg: Message) { Row { Image( painter = painterResource(R.drawable.profile_picture), contentDescription = "Contact profile picture", ) Column { Text(text = msg.author) Text(text = msg.body) } } }
ضبط التنسيق
تصميم الرسالة صحيح بالبنية الصحيحة، ولكن عناصره غير متباعدة بشكل كبير والصورة كبيرة جدًا. لإضافة عنصر قابل للإنشاء أو إعداده، تستخدم ميزة "الكتابة" أدوات التعديل. فهي تتيح لك تغيير حجم العنصر القابل للإنشاء أو تنسيقه أو مظهره أو إضافة تفاعلات عالية المستوى، مثل إتاحة إمكانية النقر على عنصر. يمكنك ربطهما معًا لإنشاء عناصر قابلة للإنشاء أكثر إفادة. ويمكنك استخدام بعضها لتحسين التصميم.
// ... import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.CircleShape import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.unit.dp @Composable fun MessageCard(msg: Message) { // Add padding around our message Row(modifier = Modifier.padding(all = 8.dp)) { Image( painter = painterResource(R.drawable.profile_picture), contentDescription = "Contact profile picture", modifier = Modifier // Set image size to 40 dp .size(40.dp) // Clip image to be shaped as a circle .clip(CircleShape) ) // Add a horizontal space between the image and the column Spacer(modifier = Modifier.width(8.dp)) Column { Text(text = msg.author) // Add a vertical space between the author and message texts Spacer(modifier = Modifier.height(4.dp)) Text(text = msg.body) } } }
// ... class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { MessageCard(Message("Android", "Jetpack Compose")) } } } data class Message(val author: String, val body: String) @Composable fun MessageCard(msg: Message) { Text(text = msg.author) Text(text = msg.body) } @Preview @Composable fun PreviewMessageCard() { MessageCard( msg = Message("Lexi", "Hey, take a look at Jetpack Compose, it's great!") ) }
// ... import androidx.compose.foundation.layout.Column @Composable fun MessageCard(msg: Message) { Column { Text(text = msg.author) Text(text = msg.body) } }
// ... import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Row import androidx.compose.ui.res.painterResource @Composable fun MessageCard(msg: Message) { Row { Image( painter = painterResource(R.drawable.profile_picture), contentDescription = "Contact profile picture", ) Column { Text(text = msg.author) Text(text = msg.body) } } }
// ... import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.CircleShape import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.unit.dp @Composable fun MessageCard(msg: Message) { // Add padding around our message Row(modifier = Modifier.padding(all = 8.dp)) { Image( painter = painterResource(R.drawable.profile_picture), contentDescription = "Contact profile picture", modifier = Modifier // Set image size to 40 dp .size(40.dp) // Clip image to be shaped as a circle .clip(CircleShape) ) // Add a horizontal space between the image and the column Spacer(modifier = Modifier.width(8.dp)) Column { Text(text = msg.author) // Add a vertical space between the author and message texts Spacer(modifier = Modifier.height(4.dp)) Text(text = msg.body) } } }
الدرس 3: التصميم المتعدد الأبعاد
تم تصميم Compose لدعم مبادئ التصميم المتعدد الأبعاد. وينفذ العديد من عناصر واجهة المستخدم الخاصة بها تنسيق Material Design بشكل غير تقليدي. في هذا الدرس، سنتحدّث عن تصميم التطبيق باستخدام التطبيقات المصغَّرة ذات التصميم المتعدد الأبعاد.
استخدام التصميم متعدد الأبعاد
يحتوي تصميم رسالتك على تنسيق الآن، لكنه لا يبدو رائعًا بعد.
يوفر Jetpack Compose تنفيذًا للإصدار Material Design 3 وعناصر واجهة المستخدم الخاصة به بشكل غير تقليدي. عليك تحسين مظهر MessageCard
القابل للإنشاء باستخدام تصميم التصميم المتعدد الأبعاد.
للبدء، عليك ربط الدالة MessageCard
بالمظهر المتعدد الأبعاد الذي تم إنشاؤه في مشروعك ComposeTutorialTheme
بالإضافة إلى Surface
.
ويمكنك تنفيذ ذلك في الدالة @Preview
والدالة setContent
. سيتيح ذلك للعناصر القابلة للإنشاء
اكتساب الأنماط كما هو محدَّد في مظهر تطبيقك، ما يضمن اتّساق المحتوى في تطبيقك.
تم تصميم التصميم المتعدد الأبعاد استنادًا إلى ثلاث ركائز: Color
وTypography
وShape
.
ستضيفها واحدة تلو الأخرى.
ملاحظة: يُنشئ نموذج نشاط الإنشاء الفارغ مظهرًا تلقائيًا لمشروعك يتيح لك تخصيص
MaterialTheme
.
إذا اخترت اسمًا مختلفًا لمشروعك عن Composetutorial، يمكنك العثور على المظهر المخصّص في ملف Theme.kt
ضمن الحزمة الفرعية ui.theme
.
// ... class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { ComposeTutorialTheme { Surface(modifier = Modifier.fillMaxSize()) { MessageCard(Message("Android", "Jetpack Compose")) } } } } } @Preview @Composable fun PreviewMessageCard() { ComposeTutorialTheme { Surface { MessageCard( msg = Message("Lexi", "Take a look at Jetpack Compose, it's great!") ) } } }
اللون
يمكنك استخدام MaterialTheme.colorScheme
للنمط باستخدام ألوان من
المظهر الالتفاف. يمكنك استخدام هذه القيم من المظهر في أي مكان تحتاج فيه إلى اللون. يستخدم هذا المثال ألوانًا ديناميكية (يتم تحديدها حسب الإعدادات المفضّلة للجهاز).
يمكنك ضبط dynamicColor
على false
في ملف MaterialTheme.kt
لتغيير ذلك.
أضِف نمطًا للعنوان وأضِف حدًا للصورة.
// ... import androidx.compose.foundation.border import androidx.compose.material3.MaterialTheme @Composable fun MessageCard(msg: Message) { Row(modifier = Modifier.padding(all = 8.dp)) { Image( painter = painterResource(R.drawable.profile_picture), contentDescription = null, modifier = Modifier .size(40.dp) .clip(CircleShape) .border(1.5.dp, MaterialTheme.colorScheme.primary, CircleShape) ) Spacer(modifier = Modifier.width(8.dp)) Column { Text( text = msg.author, color = MaterialTheme.colorScheme.secondary ) Spacer(modifier = Modifier.height(4.dp)) Text(text = msg.body) } } }
أسلوب الخط
تتوفّر أنماط الطباعة المتعدد الأبعاد في MaterialTheme
، وما عليك سوى إضافتها إلى عناصر Text
القابلة للإنشاء.
// ... @Composable fun MessageCard(msg: Message) { Row(modifier = Modifier.padding(all = 8.dp)) { Image( painter = painterResource(R.drawable.profile_picture), contentDescription = null, modifier = Modifier .size(40.dp) .clip(CircleShape) .border(1.5.dp, MaterialTheme.colorScheme.primary, CircleShape) ) Spacer(modifier = Modifier.width(8.dp)) Column { Text( text = msg.author, color = MaterialTheme.colorScheme.secondary, style = MaterialTheme.typography.titleSmall ) Spacer(modifier = Modifier.height(4.dp)) Text( text = msg.body, style = MaterialTheme.typography.bodyMedium ) } } }
شكل
يمكنك إضافة اللمسات النهائية من خلال لعبة Shape
. أولاً، عليك لفّ
النص الأساسي للرسالة حول عنصر
Surface
قابل للإنشاء. ويسمح ذلك بتخصيص شكل نص الرسالة والمسقط الرأسي. تتم أيضًا إضافة المساحة المتروكة إلى الرسالة للحصول على تنسيق أفضل.
// ... import androidx.compose.material3.Surface @Composable fun MessageCard(msg: Message) { Row(modifier = Modifier.padding(all = 8.dp)) { Image( painter = painterResource(R.drawable.profile_picture), contentDescription = null, modifier = Modifier .size(40.dp) .clip(CircleShape) .border(1.5.dp, MaterialTheme.colorScheme.primary, CircleShape) ) Spacer(modifier = Modifier.width(8.dp)) Column { Text( text = msg.author, color = MaterialTheme.colorScheme.secondary, style = MaterialTheme.typography.titleSmall ) Spacer(modifier = Modifier.height(4.dp)) Surface(shape = MaterialTheme.shapes.medium, shadowElevation = 1.dp) { Text( text = msg.body, modifier = Modifier.padding(all = 4.dp), style = MaterialTheme.typography.bodyMedium ) } } } }
تفعيل المظهر الداكن
يمكن تفعيل وضع المظهر الداكن (أو الوضع الليلي) لتجنُّب ظهور شاشة ساطعة خاصة ليلاً، أو ببساطة لتوفير بطارية الجهاز. وبفضل ميزة "التصميم المتعدد الأبعاد"، بإمكان Jetpack Compose التعامل مع المظهر الداكن تلقائيًا. ومن خلال استخدام الألوان والنصوص والخلفيات ذات التصميم المتعدد الأبعاد، ستتكيف تلقائيًا مع الخلفية الداكنة.
يمكنك إنشاء معاينات متعددة في ملفك كدوال منفصلة أو إضافة تعليقات توضيحية متعددة للدالة نفسها.
إضافة تعليق توضيحي جديد للمعاينة وتفعيل الوضع الليلي
// ... import android.content.res.Configuration @Preview(name = "Light Mode") @Preview( uiMode = Configuration.UI_MODE_NIGHT_YES, showBackground = true, name = "Dark Mode" ) @Composable fun PreviewMessageCard() { ComposeTutorialTheme { Surface { MessageCard( msg = Message("Lexi", "Hey, take a look at Jetpack Compose, it's great!") ) } } }
يتم تحديد خيارات الألوان للمظهر الفاتح والداكن في ملف Theme.kt
الذي أنشأه IDE.
حتى الآن، أنشأت عنصر واجهة مستخدم للرسالة يعرض صورة ونصَّين بأنماط مختلفة، وهو يبدو جيّدًا في المظهرَين الفاتح والداكن.
// ... import android.content.res.Configuration @Preview(name = "Light Mode") @Preview( uiMode = Configuration.UI_MODE_NIGHT_YES, showBackground = true, name = "Dark Mode" ) @Composable fun PreviewMessageCard() { ComposeTutorialTheme { Surface { MessageCard( msg = Message("Lexi", "Hey, take a look at Jetpack Compose, it's great!") ) } } }
// ... class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { ComposeTutorialTheme { Surface(modifier = Modifier.fillMaxSize()) { MessageCard(Message("Android", "Jetpack Compose")) } } } } } @Preview @Composable fun PreviewMessageCard() { ComposeTutorialTheme { Surface { MessageCard( msg = Message("Lexi", "Take a look at Jetpack Compose, it's great!") ) } } }
// ... import androidx.compose.foundation.border import androidx.compose.material3.MaterialTheme @Composable fun MessageCard(msg: Message) { Row(modifier = Modifier.padding(all = 8.dp)) { Image( painter = painterResource(R.drawable.profile_picture), contentDescription = null, modifier = Modifier .size(40.dp) .clip(CircleShape) .border(1.5.dp, MaterialTheme.colorScheme.primary, CircleShape) ) Spacer(modifier = Modifier.width(8.dp)) Column { Text( text = msg.author, color = MaterialTheme.colorScheme.secondary ) Spacer(modifier = Modifier.height(4.dp)) Text(text = msg.body) } } }
// ... @Composable fun MessageCard(msg: Message) { Row(modifier = Modifier.padding(all = 8.dp)) { Image( painter = painterResource(R.drawable.profile_picture), contentDescription = null, modifier = Modifier .size(40.dp) .clip(CircleShape) .border(1.5.dp, MaterialTheme.colorScheme.primary, CircleShape) ) Spacer(modifier = Modifier.width(8.dp)) Column { Text( text = msg.author, color = MaterialTheme.colorScheme.secondary, style = MaterialTheme.typography.titleSmall ) Spacer(modifier = Modifier.height(4.dp)) Text( text = msg.body, style = MaterialTheme.typography.bodyMedium ) } } }
// ... import androidx.compose.material3.Surface @Composable fun MessageCard(msg: Message) { Row(modifier = Modifier.padding(all = 8.dp)) { Image( painter = painterResource(R.drawable.profile_picture), contentDescription = null, modifier = Modifier .size(40.dp) .clip(CircleShape) .border(1.5.dp, MaterialTheme.colorScheme.primary, CircleShape) ) Spacer(modifier = Modifier.width(8.dp)) Column { Text( text = msg.author, color = MaterialTheme.colorScheme.secondary, style = MaterialTheme.typography.titleSmall ) Spacer(modifier = Modifier.height(4.dp)) Surface(shape = MaterialTheme.shapes.medium, shadowElevation = 1.dp) { Text( text = msg.body, modifier = Modifier.padding(all = 4.dp), style = MaterialTheme.typography.bodyMedium ) } } } }
// ... import android.content.res.Configuration @Preview(name = "Light Mode") @Preview( uiMode = Configuration.UI_MODE_NIGHT_YES, showBackground = true, name = "Dark Mode" ) @Composable fun PreviewMessageCard() { ComposeTutorialTheme { Surface { MessageCard( msg = Message("Lexi", "Hey, take a look at Jetpack Compose, it's great!") ) } } }
الدرس 4: القوائم والرسوم المتحركة
القوائم والصور المتحركة موجودة في كل مكان في التطبيقات. في هذا الدرس، ستتعرّف على كيفية الاستفادة من ميزة Compose في تسهيل عملية إنشاء القوائم وإضافة الصور المتحركة بشكل ممتع.
إنشاء قائمة بالرسائل
قد تشعر بالوحدة عند كتابة رسالة واحدة، لذلك سنغيّر المحادثة إلى أكثر من
رسالة واحدة. ستحتاج إلى إنشاء دالة Conversation
التي ستعرض رسائل متعددة. بالنسبة إلى حالة الاستخدام هذه، استخدِم
LazyColumn
في Compose و
LazyRow
. لا تعرض هذه العناصر القابلة للإنشاء سوى العناصر
المرئية على الشاشة، لذلك تم تصميمها لتكون فعّالة جدًا مع القوائم الطويلة.
في مقتطف الرمز هذا، يظهر أنّ LazyColumn
لديه items
طفل. تستخدم دالة List
كمَعلمة وتتلقّى دالة lambda
معلَمة أخرى أطلقنا عليها اسم message
(يمكننا إطلاق اسمها كما نريد)، وهذا مثال على Message
.
باختصار، تسمى هذه اللامدا لكل عنصر من قيم List
المقدَّمة. انسخ نموذج مجموعة بيانات إلى مشروعك للمساعدة في بدء المحادثة بسرعة.
// ... import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items @Composable fun Conversation(messages: List<Message>) { LazyColumn { items(messages) { message -> MessageCard(message) } } } @Preview @Composable fun PreviewConversation() { ComposeTutorialTheme { Conversation(SampleData.conversationSample) } }
أضِف تأثيرات متحركة إلى الرسائل أثناء توسيعها
أصبحت المحادثة أكثر إثارة للاهتمام. حان وقت اللعب بالرسوم المتحركة! ويمكنك أيضًا توسيع الرسالة لعرض رسالة أطول، مع تحريك حجم المحتوى ولون الخلفية معًا. لتخزين حالة واجهة المستخدم المحلية هذه، عليك تتبُّع ما إذا كان قد تم توسيع الرسالة
أم لا. لتتبُّع التغيُّر في الحالة هذه، عليك استخدام الدالتَين
remember
و
mutableStateOf
.
يمكن للدوال القابلة للإنشاء تخزين الحالة المحلية في الذاكرة باستخدام
remember
وتتبُّع التغييرات في القيمة التي تم تمريرها إلى
mutableStateOf
. إنّ العناصر القابلة للإنشاء (والعناصر الثانوية) التي تستخدم
هذه الحالة ستتم إعادة رسمها تلقائيًا عند تعديل القيمة. وتُعرف هذه العملية باسم إعادة التركيب.
باستخدام واجهات برمجة التطبيقات للحالة في Compose مثل remember
وmutableStateOf
، تؤدي أيّ تغييرات على الحالة إلى تعديل واجهة المستخدم تلقائيًا.
ملاحظة: عليك إضافة عمليات الاستيراد التالية لاستخدام بنية الموقع الإلكتروني المفوَّضة في Kotlin (الكلمة الرئيسية by
) بشكلٍ صحيح. ستؤدي الميزتان Alt+Enter أو Option+Enter إلى إضافتهما
نيابةً عنك.
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
// ... import androidx.compose.foundation.clickable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { ComposeTutorialTheme { Conversation(SampleData.conversationSample) } } } } @Composable fun MessageCard(msg: Message) { Row(modifier = Modifier.padding(all = 8.dp)) { Image( painter = painterResource(R.drawable.profile_picture), contentDescription = null, modifier = Modifier .size(40.dp) .clip(CircleShape) .border(1.5.dp, MaterialTheme.colorScheme.primary, CircleShape) ) Spacer(modifier = Modifier.width(8.dp)) // We keep track if the message is expanded or not in this // variable var isExpanded by remember { mutableStateOf(false) } // We toggle the isExpanded variable when we click on this Column Column(modifier = Modifier.clickable { isExpanded = !isExpanded }) { Text( text = msg.author, color = MaterialTheme.colorScheme.secondary, style = MaterialTheme.typography.titleSmall ) Spacer(modifier = Modifier.height(4.dp)) Surface( shape = MaterialTheme.shapes.medium, shadowElevation = 1.dp, ) { Text( text = msg.body, modifier = Modifier.padding(all = 4.dp), // If the message is expanded, we display all its content // otherwise we only display the first line maxLines = if (isExpanded) Int.MAX_VALUE else 1, style = MaterialTheme.typography.bodyMedium ) } } } }
يمكنك الآن تغيير خلفية محتوى الرسالة استنادًا إلى isExpanded
عند النقر على رسالة. ستستخدم معدِّل
clickable
لمعالجة أحداث النقر على
العنصر القابل للإنشاء. فبدلاً من التبديل بين لون الخلفية في Surface
، يمكنك تحريك لون الخلفية من خلال تعديل قيمته تدريجيًا من MaterialTheme.colorScheme.surface
إلى MaterialTheme.colorScheme.primary
والعكس صحيح. للقيام بذلك، ستستخدم الدالة animateColorAsState
. أخيرًا، ستستخدم المعدِّل animateContentSize
لتحريك حجم حاوية الرسالة بسلاسة:
// ... import androidx.compose.animation.animateColorAsState import androidx.compose.animation.animateContentSize @Composable fun MessageCard(msg: Message) { Row(modifier = Modifier.padding(all = 8.dp)) { Image( painter = painterResource(R.drawable.profile_picture), contentDescription = null, modifier = Modifier .size(40.dp) .clip(CircleShape) .border(1.5.dp, MaterialTheme.colorScheme.secondary, CircleShape) ) Spacer(modifier = Modifier.width(8.dp)) // We keep track if the message is expanded or not in this // variable var isExpanded by remember { mutableStateOf(false) } // surfaceColor will be updated gradually from one color to the other val surfaceColor by animateColorAsState( if (isExpanded) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.surface, ) // We toggle the isExpanded variable when we click on this Column Column(modifier = Modifier.clickable { isExpanded = !isExpanded }) { Text( text = msg.author, color = MaterialTheme.colorScheme.secondary, style = MaterialTheme.typography.titleSmall ) Spacer(modifier = Modifier.height(4.dp)) Surface( shape = MaterialTheme.shapes.medium, shadowElevation = 1.dp, // surfaceColor color will be changing gradually from primary to surface color = surfaceColor, // animateContentSize will change the Surface size gradually modifier = Modifier.animateContentSize().padding(1.dp) ) { Text( text = msg.body, modifier = Modifier.padding(all = 4.dp), // If the message is expanded, we display all its content // otherwise we only display the first line maxLines = if (isExpanded) Int.MAX_VALUE else 1, style = MaterialTheme.typography.bodyMedium ) } } } }
// ... import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items @Composable fun Conversation(messages: List<Message>) { LazyColumn { items(messages) { message -> MessageCard(message) } } } @Preview @Composable fun PreviewConversation() { ComposeTutorialTheme { Conversation(SampleData.conversationSample) } }
// ... import androidx.compose.foundation.clickable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { ComposeTutorialTheme { Conversation(SampleData.conversationSample) } } } } @Composable fun MessageCard(msg: Message) { Row(modifier = Modifier.padding(all = 8.dp)) { Image( painter = painterResource(R.drawable.profile_picture), contentDescription = null, modifier = Modifier .size(40.dp) .clip(CircleShape) .border(1.5.dp, MaterialTheme.colorScheme.primary, CircleShape) ) Spacer(modifier = Modifier.width(8.dp)) // We keep track if the message is expanded or not in this // variable var isExpanded by remember { mutableStateOf(false) } // We toggle the isExpanded variable when we click on this Column Column(modifier = Modifier.clickable { isExpanded = !isExpanded }) { Text( text = msg.author, color = MaterialTheme.colorScheme.secondary, style = MaterialTheme.typography.titleSmall ) Spacer(modifier = Modifier.height(4.dp)) Surface( shape = MaterialTheme.shapes.medium, shadowElevation = 1.dp, ) { Text( text = msg.body, modifier = Modifier.padding(all = 4.dp), // If the message is expanded, we display all its content // otherwise we only display the first line maxLines = if (isExpanded) Int.MAX_VALUE else 1, style = MaterialTheme.typography.bodyMedium ) } } } }
// ... import androidx.compose.animation.animateColorAsState import androidx.compose.animation.animateContentSize @Composable fun MessageCard(msg: Message) { Row(modifier = Modifier.padding(all = 8.dp)) { Image( painter = painterResource(R.drawable.profile_picture), contentDescription = null, modifier = Modifier .size(40.dp) .clip(CircleShape) .border(1.5.dp, MaterialTheme.colorScheme.secondary, CircleShape) ) Spacer(modifier = Modifier.width(8.dp)) // We keep track if the message is expanded or not in this // variable var isExpanded by remember { mutableStateOf(false) } // surfaceColor will be updated gradually from one color to the other val surfaceColor by animateColorAsState( if (isExpanded) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.surface, ) // We toggle the isExpanded variable when we click on this Column Column(modifier = Modifier.clickable { isExpanded = !isExpanded }) { Text( text = msg.author, color = MaterialTheme.colorScheme.secondary, style = MaterialTheme.typography.titleSmall ) Spacer(modifier = Modifier.height(4.dp)) Surface( shape = MaterialTheme.shapes.medium, shadowElevation = 1.dp, // surfaceColor color will be changing gradually from primary to surface color = surfaceColor, // animateContentSize will change the Surface size gradually modifier = Modifier.animateContentSize().padding(1.dp) ) { Text( text = msg.body, modifier = Modifier.padding(all = 4.dp), // If the message is expanded, we display all its content // otherwise we only display the first line maxLines = if (isExpanded) Int.MAX_VALUE else 1, style = MaterialTheme.typography.bodyMedium ) } } } }
الخطوات التالية
تهانينا، لقد أنهيت البرنامج التعليمي حول الإنشاء! لقد أنشأت شاشة محادثة بسيطة تعرض بكفاءة قائمة تضم الرسائل القابلة للتوسيع والمتحرّكة التي تحتوي على صورة ونصوص، وقد صُمّمت بالاستناد إلى مبادئ "التصميم المتعدد الأبعاد" مع مظهر داكن مع إمكانية المعاينات، وكل ذلك أقل من 100 سطر من الرموز.
إليك ما تعلمته حتى الآن:
- تعريف الدوال المكوِّنة
- عند إضافة عناصر مختلفة إلى العنصر
- هيكلة مكون واجهة المستخدم باستخدام مواد التخطيط القابلة للتعديل
- توسيع المواد الكيميائية باستخدام مفاتيح التعديل
- إنشاء قائمة فعالة
- تتبع الحالة وتعديلها
- إضافة تفاعل المستخدم على عنصر قابل للتعديل
- الرسوم المتحركة للرسائل أثناء توسيعها
إذا كنت تريد التعمّق في بعض هذه الخطوات، اطّلِع على المراجع أدناه.