‫ConstraintLayout در Compose

‫ConstraintLayout چیدمانی است که به شما امکان می‌دهد عناصر ترکیبی را نسبت‌به عناصر ترکیبی دیگر در صفحه قرار دهید. این یک جایگزین برای استفاده از چندین عنصر چیدمان سفارشی Row، Column، Box، و دیگر است.

در سیستم View، ConstraintLayout روش توصیه‌شده برای ایجاد چیدمان‌های بزرگ و پیچیده بود، زیرا سلسله‌مراتب نمای تخت برای عملکرد بهتر از نمای تودرتو است. بااین‌حال، این موضوع در «نوشتن» که می‌تواند سلسله‌مراتب چیدمان عمیق را به‌طور کارآمد مدیریت کند، نگران‌کننده نیست، بنابراین ConstraintLayout به این اندازه مفید نیست.

با ConstraintLayout شروع کنید

برای استفاده از ConstraintLayout در «نوشتن»، باید این وابستگی را در build.gradle (علاوه‌بر راه‌اندازی «نوشتن») اضافه کنید:

implementation "androidx.constraintlayout:constraintlayout-compose:$constraintlayout_compose_version"

ConstraintLayout در «نوشتن» به این روش بااستفاده از DSL کار می‌کند:

  • بااستفاده از createRefs() یا createRefFor()، برای هر عنصر ترکیب‌شدنی در ConstraintLayout مرجع ایجاد کنید.
  • محدودیت‌ها بااستفاده از تغییردهنده constrainAs() ارائه می‌شوند که مرجع را به‌عنوان پارامتر می‌گیرد و به شما امکان می‌دهد محدودیت‌های آن را در لامبدای بدنه مشخص کنید.
  • محدودیت‌ها بااستفاده از 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 را با حاشیه 16.dp به پایین Button محدود می‌کند.

دکمه در بالای نوشتار نشان داده می‌شود
شکل ۱. یک Button و یک Text ترکیبی که در یک ConstraintLayout به یکدیگر محدود شده‌اند.

Decoupled API

در 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 حاوی مفاهیمی مانند دستورالعمل‌ها، موانع، و زنجیره‌ها است که می‌تواند به قرار دادن عناصر در داخل عنصر ترکیبی شما کمک کند.

رهنمودها

خطوط راهنما کمک‌های دیداری کوچکی برای طراحی چیدمان‌ها هستند. عناصر ترکیبی را می‌توان به یک خط راهنما محدود کرد. دستورالعمل‌ها برای قرار دادن عناصر در یک 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: فضا قبل‌از اولین و بعداز آخرین عنصر ترکیبی توزیع می‌شود، عناصر ترکیبی بدون فاصله بین یکدیگر کنار هم قرار می‌گیرند.