فيديو تعليمي

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

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

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

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

الدرس 1: الدوال القابلة للتعديل

تم تصميم Jetpack Compose استنادًا إلى الدوال القابلة للإنشاء. تتيح لك هذه الدوال تحديد واجهة مستخدم التطبيق برمجيًا من خلال وصف الشكل الذي ينبغي أن يبدو عليه وتوفير تبعيات البيانات، بدلاً من التركيز على عملية إنشاء واجهة المستخدم (تهيئة عنصر ما، إرفاقه بأحد الوالدين، وما إلى ذلك). لإنشاء دالة قابلة للإنشاء، ما عليك سوى إضافة تعليق توضيحي @Composable لاسم الدالة

إضافة عنصر نصي

للبدء، قم بتنزيل أحدث إصدار من يمكنك إنشاء تطبيق Android Studio من خلال اختيار مشروع جديد، وضمن فئة الهاتف والجهاز اللوحي، اختَر إفراغ النشاط. أدخِل اسمًا للتطبيق ComposeTutorial وانقر على إنهاء. الإعداد التلقائي يحتوي القالب بالفعل على بعض عناصر Compose، ولكن في هذا البرنامج التعليمي ستنشئه خطوة خطوة بخطوة.

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

الدرس 2: التنسيقات

عناصر واجهة المستخدم تسلسلية، وتحتوي على عناصر مضمنة في عناصر أخرى. في 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 تنفيذًا للتصميم المتعدد الأبعاد 3 (Material Design) وعناصر واجهة المستخدم التابعة له خارج . سيتم تحسين مظهر "MessageCard" قابلة للإنشاء باستخدام تصميم متعدد الأبعاد.

للبدء، يجب إحاطة الدالة MessageCard بالرمز تم إنشاء مظهر Material Design في مشروعك، 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 التعامل مع العقبات المظهر تلقائيًا. إن استخدام ألوان ونصوص وخلفيات Material Design مع الخلفية الداكنة.

يمكنك إنشاء معاينات متعددة في ملفك كدوال منفصلة، أو إضافة عدة معاينات التعليقات التوضيحية لنفس الوظيفة.

يمكنك إضافة تعليق توضيحي جديد للمعاينة وتفعيل "الوضع الليلي".

// ...
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!")
      )
    }
   }
}
  
عرض المعاينة
إخفاء المعاينة

يتم تحديد خيارات الألوان للمظاهر الفاتحة والداكنة في بيئة التطوير المتكاملة (IDE) ملف Theme.kt.

لقد أنشأت حتى الآن عنصرًا في واجهة مستخدم رسالة يعرض صورة ونصين مختلفين ويبدو جيدًا في المظهرَين الفاتح والداكن.

// ...
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: القوائم والرسوم المتحركة

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

إنشاء قائمة بالرسائل

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

في مقتطف الرمز هذا، يمكنك رؤية أن لدى LazyColumn طفل واحد (items) يستغرق الأمر الدالة List كمَعلمة وlambda يستقبل معلمة أطلقنا عليها اسم message (يمكننا الحصول على سمناها كما نريد) وهو مثال على Message. باختصار، يتم استدعاء هذه دالة lambda لكل عنصر من العناصر 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.