إنشاء أساسيات التصميم

يسهّل Jetpack Compose إلى حد كبير تصميم واجهة مستخدِم تطبيقك وإنشاؤها. يحوّل Compose الحالة إلى عناصر واجهة مستخدِم من خلال ما يلي:

  1. تأليف العناصر
  2. تنسيق العناصر
  3. رسم العناصر

تحويل حالة Compose إلى واجهة مستخدم من خلال التركيب والتصميم والرسم

يركّز هذا المستند على تنسيق العناصر، ويشرح بعض الوحدات الأساسية التي يوفّرها Compose لمساعدتك في تنسيق عناصر واجهة المستخدِم.

أهداف التنسيقات في Compose

يستهدف تنفيذ نظام التنسيق في Jetpack Compose هدفَين رئيسيَّين:

أساسيات الدوال المُركّبة

الدوال المُركّبة هي الوحدة الأساسية في Compose. الدالة المُركّبة هي دالة تعرض Unit وتصف جزءًا من واجهة المستخدِم. تستخدم الدالة بعض الإدخالات وتنشئ ما يظهر على الشاشة. لمزيد من المعلومات عن الدوال المُركّبة، يمكنك الاطّلاع على مستند النموذج الذهني في Compose model.

قد تعرض الدالة المُركّبة عدة عناصر واجهة مستخدِم. ومع ذلك، إذا لم تقدّم إرشادات حول كيفية ترتيبها، قد يرتب Compose العناصر بطريقة لا تعجبك. على سبيل المثال، ينشئ هذا الرمز عنصرَين نصيَّين:

@Composable
fun ArtistCard() {
    Text("Alfred Sisley")
    Text("3 minutes ago")
}

بدون إرشادات حول كيفية ترتيبها، يكدّس Compose العناصر النصية فوق بعضها البعض، ما يجعلها غير قابلة للقراءة:

عنصران نصيان مرسومَين فوق بعضهما البعض، ما يجعل النص غير قابل للقراءة

يوفّر Compose مجموعة من التنسيقات الجاهزة للاستخدام لمساعدتك في ترتيب عناصر واجهة المستخدِم، ويسهّل عليك تحديد تنسيقاتك الخاصة الأكثر تخصّصًا.

مكوّنات التنسيق العادية

في كثير من الحالات، يمكنك استخدام عناصر التنسيق العادية في Compose's standard layout elements فقط.

استخدِم Column لوضع العناصر عموديًا على الشاشة.

@Composable
fun ArtistCardColumn() {
    Column {
        Text("Alfred Sisley")
        Text("3 minutes ago")
    }
}

عنصران نصيان مرتّبان في تخطيط عمودي، ما يتيح قراءة النص

وبالمثل، استخدِم Row لوضع العناصر أفقيًا على الشاشة. تتيح كل من Column وRow ضبط محاذاة العناصر التي تحتوي عليها.

@Composable
fun ArtistCardRow(artist: Artist) {
    Row(verticalAlignment = Alignment.CenterVertically) {
        Image(bitmap = artist.image, contentDescription = "Artist image")
        Column {
            Text(artist.name)
            Text(artist.lastSeenOnline)
        }
    }
}

تعرض تخطيطًا أكثر تعقيدًا، مع رسم صغير بجانب عمود من عناصر النص

استخدِم Box لوضع العناصر فوق بعضها البعض. تتيح Box أيضًا ضبط محاذاة محدّدة للعناصر التي تحتوي عليها.

@Composable
fun ArtistAvatar(artist: Artist) {
    Box {
        Image(bitmap = artist.image, contentDescription = "Artist image")
        Icon(Icons.Filled.Check, contentDescription = "Check mark")
    }
}

تعرض هذه السمة عنصرَين مكدّسَين فوق بعضهما البعض

غالبًا ما تكون هذه الوحدات الأساسية كل ما تحتاجه. يمكنك كتابة دالة مُركّبة خاصة بك لدمج هذه التنسيقات في تنسيق أكثر تفصيلاً يناسب تطبيقك.

مقارنة بين ثلاثة عناصر بسيطة قابلة للإنشاء في التنسيق: العمود والصف والمربّع

لضبط موضع العناصر الثانوية ضمن Row، اضبط الوسيطتَين horizontalArrangement وverticalAlignment. بالنسبة إلى Column، اضبط الوسيطتَين verticalArrangement وhorizontalAlignment:

@Composable
fun ArtistCardArrangement(artist: Artist) {
    Row(
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.End
    ) {
        Image(bitmap = artist.image, contentDescription = "Artist image")
        Column { /*...*/ }
    }
}

العناصر محاذية إلى اليمين

نموذج التنسيق

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

باختصار، تقيس العُقد الرئيسية حجمها قبل العناصر الثانوية، ولكن يتم تغيير حجمها ووضعها بعد العناصر الثانوية.

لنأخذ على سبيل المثال الدالة SearchResult التالية.

@Composable
fun SearchResult() {
    Row {
        Image(
            // ...
        )
        Column {
            Text(
                // ...
            )
            Text(
                // ...
            )
        }
    }
}

تنتج هذه الدالة شجرة واجهة المستخدِم التالية.

SearchResult
  Row
    Image
    Column
      Text
      Text

في مثال SearchResult، يتّبع تنسيق شجرة واجهة المستخدِم هذا الترتيب:

  1. يُطلب من العقدة الجذر Row قياس حجمها.
  2. تطلب العقدة الجذر Row من أول عنصر ثانوي لها، وهو Image، قياس حجمه.
  3. Image هي عقدة ورقية (أي ليس لها عناصر ثانوية)، لذا تعرض حجمًا وتعرض تعليمات الموضع.
  4. تطلب العقدة الجذر Row من ثاني عنصر ثانوي لها، وهو Column، قياس حجمه.
  5. تطلب العقدة Column من أول عنصر ثانوي لها، وهو Text، قياس حجمه.
  6. العقدة الأولى Text هي عقدة ورقية، لذا تعرض حجمًا وتعرض تعليمات الموضع.
  7. تطلب العقدة Column من ثاني عنصر ثانوي لها، وهو Text، قياس حجمه.
  8. العقدة الثانية Text هي عقدة ورقية، لذا تعرض حجمًا وتعرض تعليمات الموضع.
  9. بعد أن تقيس العقدة Column حجم العناصر الثانوية وتغيّر حجمها وتضعها، يمكنها تحديد حجمها وموضعها.
  10. بعد أن تقيس العقدة الجذر Row حجم العناصر الثانوية وتغيّر حجمها وتضعها، يمكنها تحديد حجمها وموضعها.

ترتيب القياسات والأحجام والمواضع في شجرة واجهة مستخدم نتائج البحث

الأداء

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

إذا كان التنسيق يحتاج إلى عمليات قياس متعدّدة لسبب ما، يوفّر Compose نظامًا خاصًا، وهو القياسات الجوهرية. يمكنك قراءة المزيد عن هذه الميزة في القياسات الجوهرية في تنسيقات Compose.

بما أنّ القياس والموضع هما مرحلتان فرعيتان مختلفتان من مرحلة التنسيق، يمكن تنفيذ أي تغييرات تؤثر في موضع العناصر فقط، وليس في القياس، بشكل منفصل.

استخدام المعدِّلات في تنسيقاتك

كما هو موضّح في معدِّلات Compose، يمكنك استخدام المعدِّلات لتزيين الدوال المُركّبة أو زيادتها. المعدِّلات ضرورية لتخصيص التنسيق. على سبيل المثال، نربط هنا عدة معدِّلات لتخصيص ArtistCard:

@Composable
fun ArtistCardModifiers(
    artist: Artist,
    onClick: () -> Unit
) {
    val padding = 16.dp
    Column(
        Modifier
            .clickable(onClick = onClick)
            .padding(padding)
            .fillMaxWidth()
    ) {
        Row(verticalAlignment = Alignment.CenterVertically) { /*...*/ }
        Spacer(Modifier.size(padding))
        Card(
            elevation = CardDefaults.cardElevation(defaultElevation = 4.dp),
        ) { /*...*/ }
    }
}

تخطيط أكثر تعقيدًا، باستخدام أدوات تعديل لتغيير طريقة ترتيب الرسومات والمناطق التي تستجيب لبيانات أدخلها المستخدم

في الرمز أعلاه، لاحظ استخدام دوال معدِّلات مختلفة معًا.

  • تجعل الدالة clickable الدالة المُركّبة تتفاعل مع بيانات أدخلها المستخدم وتعرض تموّجًا.
  • تضع الدالة padding مساحة حول أحد العناصر.
  • تجعل الدالة fillMaxWidth الدالة المُركّبة تملأ الحد الأقصى للعرض الذي يمنحه لها العنصر الرئيسي.
  • تحدّد الدالة size() العرض والارتفاع المفضّلين لعنصر ما.

التنسيقات القابلة للتمرير

يمكنك التعرّف أكثر على التنسيقات القابلة للتمرير في الـ مستند إيماءات Compose.

بالنسبة إلى القوائم والقوائم الكسولة، يمكنك الاطّلاع على مستند قوائم Compose.

التنسيقات السريعة الاستجابة

يجب تصميم التنسيق مع مراعاة اتجاهات الشاشة المختلفة وأحجام عامل الشكل. يوفّر Compose بعض الآليات الجاهزة لتسهيل تكييف تنسيقاتك المُركّبة مع إعدادات الشاشة المختلفة.

القيود

لمعرفة القيود الواردة من العنصر الرئيسي وتصميم التنسيق وفقًا لذلك، يمكنك استخدام BoxWithConstraints. يمكن العثور على قيود القياس في نطاق تعبير lambda للمحتوى. يمكنك استخدام قيود القياس هذه لإنشاء تنسيقات مختلفة لإعدادات الشاشة المختلفة:

@Composable
fun WithConstraintsComposable() {
    BoxWithConstraints {
        Text("My minHeight is $minHeight while my maxWidth is $maxWidth")
    }
}

التنسيقات المستندة إلى المواضع

يوفّر Compose مجموعة كبيرة ومتنوّعة من الدوال المُركّبة المستندة إلى التصميم المتعدد الأبعاد مع الاعتمادية androidx.compose.material:material (المضمّنة عند إنشاء مشروع Compose في استوديو Android) لتسهيل إنشاء واجهة المستخدِم. يتم توفير عناصر مثل Drawer، FloatingActionButton، و TopAppBar.

تستخدم مكوّنات Material بشكل كبير واجهات برمجة تطبيقات المواضع، وهو نمط يقدّمه Compose لإضافة طبقة من التخصيص فوق الدوال المُركّبة. يجعل هذا النهج المكوّنات أكثر مرونة، لأنّها تقبل عنصرًا ثانويًا يمكنه ضبط نفسه بدلاً من الاضطرار إلى عرض كل مَعلمة إعداد للعنصر الثانوي. تترك المواضع مساحة فارغة في واجهة المستخدِم ليملأها المطوّر كما يشاء. على سبيل المثال، هذه هي المواضع التي يمكنك تخصيصها في TopAppBar:

مخطّط بياني يعرض الخانات المتاحة في شريط تطبيق "مكوّنات Material"

تأخذ الدوال المُركّبة عادةً تعبير lambda مُركّبًا content ( content: @Composable () -> Unit). تعرض واجهات برمجة تطبيقات المواضع مَعلمات content متعدّدة لاستخدامات محدّدة. على سبيل المثال، يتيح لك TopAppBar تقديم المحتوى لـ title وnavigationIcon وactions.

على سبيل المثال، Scaffold يتيح لك تنفيذ واجهة مستخدِم باستخدام بنية التنسيق الأساسية في التصميم المتعدد الأبعاد. Scaffoldيوفّر مواضعًا لمكوّنات التصميم المتعدد الأبعاد الأكثر شيوعًا على المستوى الأعلى، مثل TopAppBar، BottomAppBar، FloatingActionButton، و Drawer. باستخدام Scaffold، من السهل التأكّد من وضع هذه المكوّنات بشكل صحيح وعملها معًا بشكل سليم.

تطبيق JetNews التجريبي الذي يستخدم Scaffold لتحديد موضع عناصر متعددة

@Composable
fun HomeScreen(/*...*/) {
    ModalNavigationDrawer(drawerContent = { /* ... */ }) {
        Scaffold(
            topBar = { /*...*/ }
        ) { contentPadding ->
            // ...
        }
    }
}