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

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

  1. تركيب العناصر
  2. تخطيط العناصر
  3. رسم العناصر

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

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

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

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

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

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

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

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

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

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

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

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

في كثير من الحالات، يمكنك استخدام عناصر التنسيق العادية في Compose.

استخدِم 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.

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

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

As discussed in Compose modifiers, you can use modifiers to decorate or augment your composables. تُعدّ المُعدِّلات ضرورية لتخصيص التنسيق. على سبيل المثال، نربط هنا عدة معدِّلات لتخصيص 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 Components

تستخدم الدوال المركّبة عادةً دالة 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 ->
            // ...
        }
    }
}