فيديو تعليمي

الدليل التعليمي من Jetpack Compose

Jetpack Compose هي مجموعة أدوات حديثة لإنشاء واجهة مستخدم Android الأصلية. يساعد Jetpack Compose في تبسيط عملية تطوير واجهة المستخدم على Android وتسريعها باستخدام رموز برمجية أقل وأدوات فعّالة وواجهات برمجة تطبيقات سهلة الاستخدام في Kotlin.

في هذا البرنامج التعليمي، سننشئ مكوّنًا بسيطًا لواجهة المستخدم يحتوي على دوال تعريفية. ولن يتم تعديل أي تنسيقات XML أو استخدام "محرِّر التنسيق". بدلاً من ذلك، ستطلب دوال قابلة للإنشاء لتحديد العناصر التي تريدها، وستتولى أداة التجميع Compose الباقي.

معاينة كاملة
معاينة كاملة

الدرس الأول: الدوال القابلة للإنشاء

تم تصميم 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. لتعديل المعاينات في أي وقت، انقر على زر إعادة التحميل في أعلى نافذة المعاينة.

معاينة دالة قابلة للإنشاء في "استوديو Android"
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")
}
  
عرض المعاينة
إخفاء المعاينة
معاينة دالة قابلة للإنشاء في "استوديو 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!")
    )
}

  
عرض المعاينة
إخفاء المعاينة

تُنشئ هذه التعليمة البرمجية عنصرين نصيين داخل عرض المحتوى. وبما أنّك لم تقدّم أي معلومات حول طريقة ترتيبها، يتم رسم عناصر النص فوق بعضها، ما يجعل النص غير قابل للقراءة.

استخدام عمود

تتيح لك الدالة Column ترتيب العناصر عموديًا. أضِف Column إلى الدالة MessageCard.
يمكنك استخدام Row لترتيب العناصر أفقيًا و Box لتجميع العناصر.

// ...
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 سطر من الرموز.

إليك ما تعلمته حتى الآن:

  • تعريف الدوال المكوِّنة
  • عند إضافة عناصر مختلفة إلى العنصر
  • هيكلة مكون واجهة المستخدم باستخدام مواد التخطيط القابلة للتعديل
  • توسيع المواد الكيميائية باستخدام مفاتيح التعديل
  • إنشاء قائمة فعالة
  • تتبع الحالة وتعديلها
  • إضافة تفاعل المستخدم على عنصر قابل للتعديل
  • الرسوم المتحركة للرسائل أثناء توسيعها

إذا كنت تريد التعمّق في بعض هذه الخطوات، اطّلِع على المراجع أدناه.

الخطوات التالية

ضبط إعدادات الجهاز
الآن وبعد الانتهاء من البرنامج التعليمي حول "إنشاء"، أصبحت جاهزًا لبدء الإنشاء باستخدام "الإنشاء".
مسار
اطّلِع على مجموعة من الفيديوهات التعليمية والدروس التطبيقية حول الترميز التي ستساعدك في تعلّم وإتقان استخدام Jetpack Compose.