ConstraintLayout في Compose

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

يمكنك استخدام ConstraintLayout في السيناريوهات التالية:

  • لتجنُّب دمج عدة قيَم Column وRow في موضع العناصر على الشاشة من أجل تحسين سهولة قراءة الرمز
  • لضبط موضع المواد القابلة للتركيب بالنسبة إلى المواد الأخرى أو موضعها بناءً على الإرشادات أو الحواجز أو السلاسل

في نظام "العرض"، كانت الطريقة ConstraintLayout هي الطريقة المقترَحة لإنشاء تنسيقات كبيرة ومعقدة، لأنّ التسلسل الهرمي للعرض المسطّح كان أفضل من حيث الأداء مقارنةً بالعروض المتداخلة. ومع ذلك، لا داعي للقلق في ميزة Compose، لأنّها قادرة على التعامل بكفاءة مع التسلسلات الهرمية للتخطيط العميق.

بدء استخدام ConstraintLayout

لاستخدام ConstraintLayout في Compose، عليك إضافة هذه الاعتمادية إلى build.gradle (بالإضافة إلى إعداد Compose):

implementation "androidx.constraintlayout:constraintlayout-compose:1.0.1"

تعمل ConstraintLayout في Compose على النحو التالي باستخدام DSL:

  • أنشئ مراجع لكل عنصر يمكن إنشاؤه في ConstraintLayout باستخدام createRefs() أو createRefFor().
  • يتمّ وضع القيود باستخدام مفتاح التعديل constrainAs() الذي يأخذ المرجع كمَعلمة ويتيح لك تحديد قيوده في نص lambda.
  • يتم تحديد القيود باستخدام linkTo() أو غيرها من الطرق المفيدة.
  • إنّ parent هو مرجع حالي يمكن استخدامه لتحديد قيود في ما يتعلّق بالعنصر ConstraintLayout القابل للإنشاء نفسه.

في ما يلي مثال على مادة عرض قابلة للإنشاء باستخدام ConstraintLayout:

@Composable
fun ConstraintLayoutContent() {
    ConstraintLayout {
        // Create references for the composables to constrain
        val (button, text) = createRefs()

        Button(
            onClick = { /* Do something */ },
            // Assign reference "button" to the Button composable
            // and constrain it to the top of the ConstraintLayout
            modifier = Modifier.constrainAs(button) {
                top.linkTo(parent.top, margin = 16.dp)
            }
        ) {
            Text("Button")
        }

        // Assign reference "text" to the Text composable
        // and constrain it to the bottom of the Button composable
        Text(
            "Text",
            Modifier.constrainAs(text) {
                top.linkTo(button.bottom, margin = 16.dp)
            }
        )
    }
}

يؤدي هذا الرمز إلى حصر الجزء العلوي من Button في العنصر الرئيسي باستخدام هامش 16.dp وText في أسفل Button أيضًا مع هامش 16.dp.

تعرض زرًا وعنصرًا نصيًا مرتبة في ConstraintLayout

واجهة برمجة التطبيقات المنفصلة

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

في مثل هذه الحالات، يمكنك استخدام ConstraintLayout بطريقة مختلفة:

  1. أدخِل ConstraintSet كمَعلمة إلى ConstraintLayout.
  2. حدِّد المراجع التي تم إنشاؤها في ConstraintSet للمواد القابلة للإنشاء باستخدام المعدِّل layoutId.

@Composable
fun DecoupledConstraintLayout() {
    BoxWithConstraints {
        val constraints = if (minWidth < 600.dp) {
            decoupledConstraints(margin = 16.dp) // Portrait constraints
        } else {
            decoupledConstraints(margin = 32.dp) // Landscape constraints
        }

        ConstraintLayout(constraints) {
            Button(
                onClick = { /* Do something */ },
                modifier = Modifier.layoutId("button")
            ) {
                Text("Button")
            }

            Text("Text", Modifier.layoutId("text"))
        }
    }
}

private fun decoupledConstraints(margin: Dp): ConstraintSet {
    return ConstraintSet {
        val button = createRefFor("button")
        val text = createRefFor("text")

        constrain(button) {
            top.linkTo(parent.top, margin = margin)
        }
        constrain(text) {
            top.linkTo(button.bottom, margin)
        }
    }
}

وبعد ذلك، يمكنك ضبط قيمة مختلفة للسمة ConstraintSet عندما تحتاج إلى تغيير القيود.

مفاهيم ConstraintLayout

يحتوي ConstraintLayout على مفاهيم مثل الإرشادات والحواجز والسلاسل التي يمكن أن تساعد في تحديد موضع العناصر داخل Composable.

الإرشادات

الإرشادات هي مساعدات مرئية صغيرة لتصميم التخطيطات باستخدامها. يمكن تقييد العناصر القابلة للإنشاء وفقًا لإرشادات الجودة. الإرشادات مفيدة لتحديد موضع العناصر في dp أو percentage معينة داخل العنصر الرئيسي القابل للإنشاء.

هناك نوعان مختلفان من الإرشادات، وهما رأسي وأفقي. أمّا العمودان الأفقيان هما top وbottom، والعمودان العمودان start وend.

ConstraintLayout {
    // Create guideline from the start of the parent at 10% the width of the Composable
    val startGuideline = createGuidelineFromStart(0.1f)
    // Create guideline from the end of the parent at 10% the width of the Composable
    val endGuideline = createGuidelineFromEnd(0.1f)
    //  Create guideline from 16 dp from the top of the parent
    val topGuideline = createGuidelineFromTop(16.dp)
    //  Create guideline from 16 dp from the bottom of the parent
    val bottomGuideline = createGuidelineFromBottom(16.dp)
}

لإنشاء إرشادات، استخدِم createGuidelineFrom* مع نوع الإرشادات المطلوبة. سيؤدي ذلك إلى إنشاء مرجع يمكن استخدامه في مجموعة Modifier.constrainAs().

الحواجز

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

لإنشاء عائق، استخدِم createTopBarrier() (أو: createBottomBarrier() أو createEndBarrier() أو createStartBarrier()) وقدِّم المراجع التي تشكّل عائقًا.

ConstraintLayout {
    val constraintSet = ConstraintSet {
        val button = createRefFor("button")
        val text = createRefFor("text")

        val topBarrier = createTopBarrier(button, text)
    }
}

ويمكن بعد ذلك استخدام الحاجز في مربّع Modifier.constrainAs().

السلاسل

توفّر السلاسل سلوكًا يشبه المجموعة في محور واحد (أفقيًا أو عموديًا). ويمكن تقييد المحور الآخر بشكل مستقل.

لإنشاء سلسلة، استخدِم createVerticalChain أو createHorizontalChain:

ConstraintLayout {
    val constraintSet = ConstraintSet {
        val button = createRefFor("button")
        val text = createRefFor("text")

        val verticalChain = createVerticalChain(button, text, chainStyle = ChainStyle.Spread)
        val horizontalChain = createHorizontalChain(button, text)
    }
}

ويمكن بعد ذلك استخدام السلسلة في مربّع Modifier.constrainAs().

يمكن ضبط سلسلة باستخدام ChainStyles مختلفة، ما يحدِّد كيفية التعامل مع المساحة المحيطة بعنصر مركّب، مثل:

  • ChainStyle.Spread: يتم توزيع المساحة بالتساوي على جميع المواد القابلة للتركيب، بما في ذلك المساحة الخالية قبل أول مادة قابلة لتركيبها وبعد آخر مادة مركّبة.
  • ChainStyle.SpreadInside: يتم توزيع المساحة بالتساوي على جميع المواد المركبة، بدون أي مساحة خالية قبل أول مادة قابلة للتركيب أو بعد آخر مادة قابلة للتكوين.
  • ChainStyle.Packed: يتم توزيع المساحة قبل الأول وبعد آخر مادة قابلة للتركيب، ويتم تجميعها معًا بدون مساحة بين بعضها البعض.

مزيد من المعلومات

يمكنك الاطّلاع على مزيد من المعلومات حول ConstraintLayout في "Compose" من واجهات برمجة التطبيقات قيد التشغيل في نماذج Compose التي تستخدم ConstraintLayout.