ConstraintLayout در Compose

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

استفاده از ConstraintLayout را در سناریوهای زیر در نظر بگیرید:

  • برای جلوگیری از تو در تو قرار دادن چندین Column و Row برای موقعیت‌یابی عناصر روی صفحه و بهبود خوانایی کد.
  • برای قرار دادن ترکیبات قابل ترکیب نسبت به سایر ترکیبات قابل ترکیب یا قرار دادن ترکیبات قابل ترکیب بر اساس خطوط راهنما، موانع یا زنجیرها.

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

شروع کار با ConstraintLayout

برای استفاده از ConstraintLayout در Compose، باید این وابستگی را در build.gradle خود (علاوه بر تنظیمات Compose ) اضافه کنید:

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

ConstraintLayout در Compose با استفاده از DSL به روش زیر عمل می‌کند:

  • با استفاده از createRefs() یا createRefFor() برای هر composable در ConstraintLayout ارجاع ایجاد کنید.
  • محدودیت‌ها با استفاده از اصلاح‌کننده constrainAs() ارائه می‌شوند، که مرجع را به عنوان پارامتر می‌گیرد و به شما امکان می‌دهد محدودیت‌های آن را در بدنه لامبدا مشخص کنید.
  • محدودیت‌ها با استفاده از linkTo() یا سایر متدهای مفید مشخص می‌شوند.
  • parent یک مرجع موجود است که می‌تواند برای تعیین محدودیت‌هایی نسبت به خودِ ConstraintLayout composable مورد استفاده قرار گیرد.

در اینجا مثالی از یک composable با استفاده از 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 به یکدیگر محدود شده‌اند.

API جدا شده

در مثال ConstraintLayout ، قیدها به صورت درون‌خطی مشخص می‌شوند و یک اصلاح‌کننده در ترکیب‌بندی که به آن اعمال می‌شوند، وجود دارد. با این حال، موقعیت‌هایی وجود دارد که ترجیح داده می‌شود قیدها را از طرح‌بندی‌هایی که به آنها اعمال می‌شوند، جدا کنید. به عنوان مثال، ممکن است بخواهید قیدها را بر اساس پیکربندی صفحه تغییر دهید، یا بین دو مجموعه قید، انیمیشن ایجاد کنید.

برای مواردی از این دست، می‌توانید از ConstraintLayout به روش دیگری استفاده کنید:

  1. یک ConstraintSet را به عنوان پارامتر به ConstraintLayout ارسال کنید.
  2. ارجاع‌های ایجاد شده در ConstraintSet را با استفاده از اصلاح‌کننده layoutId به composableها اختصاص دهید.

@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 شما کمک کنند.

دستورالعمل‌ها

خطوط راهنما، ابزارهای بصری کوچکی هستند که به طراحی طرح‌بندی‌ها کمک می‌کنند. Composableها را می‌توان به یک خط راهنما محدود کرد. خطوط راهنما برای قرار دادن عناصر در یک dp یا percentage مشخص درون Composable والد مفید هستند.

دو نوع خط راهنما وجود دارد، عمودی و افقی. دو خط افقی 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 مختلف پیکربندی کرد، که نحوه برخورد با فضای اطراف یک composable را تعیین می‌کنند، مانند:

  • ChainStyle.Spread : فضا به طور مساوی در تمام composableها توزیع می‌شود، از جمله فضای خالی قبل از اولین composable و بعد از آخرین composable.
  • ChainStyle.SpreadInside : فضا به طور مساوی در تمام composableها توزیع می‌شود، بدون هیچ فضای خالی قبل از اولین composable یا بعد از آخرین composable.
  • ChainStyle.Packed : فضا قبل از اولین و بعد از آخرین composable توزیع می‌شود، composableها بدون فاصله بین یکدیگر، در کنار هم قرار می‌گیرند.

بیشتر بدانید

برای کسب اطلاعات بیشتر در مورد ConstraintLayout در Compose، به APIهای موجود در نمونه‌های Compose که از ConstraintLayout استفاده می‌کنند، مراجعه کنید.

{% کلمه به کلمه %} {% فعل کمکی %} {% کلمه به کلمه %} {% فعل کمکی %}