Compose'da ConstraintLayout

ConstraintLayout, composable'ları ekrandaki diğer composable'lara göre yerleştirmenize olanak tanıyan bir düzendir. Bu, iç içe yerleştirilmiş birden fazla Row, Column, Box ve diğer özel düzen öğelerini kullanmaya alternatif bir yöntemdir.

Görünüm sisteminde, düz görünüm hiyerarşisi iç içe yerleştirilmiş görünümlere kıyasla performans açısından daha iyi olduğundan büyük ve karmaşık düzenler oluşturmak için ConstraintLayout önerilen yöntemdi. Ancak bu durum, derin düzen hiyerarşilerini verimli bir şekilde işleyebilen Compose'da sorun teşkil etmez. Bu nedenle ConstraintLayout, Compose'da o kadar da faydalı değildir.

ConstraintLayout çözümünü kullanmaya başlayın

Oluşturma'da ConstraintLayout kullanmak için build.gradle dosyanıza (Compose kurulumuna ek olarak) şu bağımlılığı eklemeniz gerekir:

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

Oluşturma bölümündeki ConstraintLayout, DSL kullanılarak aşağıdaki şekilde çalışır:

  • ConstraintLayout içindeki her composable için createRefs() veya createRefFor() kullanarak referans oluşturun.
  • Kısıtlamalar, referansı parametre olarak alan ve gövde lambda'sında kısıtlamalarını belirtmenize olanak tanıyan constrainAs() değiştiricisi kullanılarak sağlanır.
  • Kısıtlamalar, linkTo() veya diğer faydalı yöntemler kullanılarak belirtilir.
  • parent, ConstraintLayout composable'ın kendisiyle ilgili kısıtlamaları belirtmek için kullanılabilecek mevcut bir referanstır.

ConstraintLayout kullanan bir composable örneğini aşağıda bulabilirsiniz:

@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)
            }
        )
    }
}

Bu kod, Button öğesinin üst kısmını 16.dp kenar boşluğuyla üst öğeye, Text öğesini de Button öğesinin alt kısmına 16.dp kenar boşluğuyla kısıtlar.

Düğme, metnin üzerinde görünür.
Şekil 1. Button ve Text, ConstraintLayout içinde birbirine bağlı olarak oluşturulabilir.

Ayrılmış API

ConstraintLayout örneğinde kısıtlamalar satır içi olarak belirtilir ve uygulandıkları composable'da bir değiştirici bulunur. Ancak, kısıtlamaları uygulandıkları düzenlerden ayırmanın tercih edildiği durumlar vardır. Örneğin, kısıtlamaları ekran yapılandırmasına göre değiştirmek veya iki kısıtlama grubu arasında animasyon oluşturmak isteyebilirsiniz.

Bu gibi durumlarda ConstraintLayout simgesini farklı bir şekilde kullanabilirsiniz:

  1. ConstraintLayout işlevine parametre olarak ConstraintSet iletin.
  2. ConstraintSet içinde oluşturulan referansları, layoutId değiştiricisini kullanarak composable'lara atayın.

@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)
        }
    }
}

Ardından, kısıtlamaları değiştirmeniz gerektiğinde farklı bir ConstraintSet iletebilirsiniz.

ConstraintLayout kavramları

ConstraintLayout, composable'ınızdaki öğeleri konumlandırmanıza yardımcı olabilecek yönergeler, engeller ve zincirler gibi kavramlar içerir.

Kurallar

Kılavuzlar, düzen tasarlarken kullanılan küçük görsel yardımcı araçlardır. Composable'lar bir kılavuz çizgisiyle sınırlandırılabilir. Kılavuzlar, öğeleri üst composable'ın içinde belirli bir dp veya percentage konumuna yerleştirmek için kullanışlıdır.

Dikey ve yatay olmak üzere iki farklı türde yönergeler vardır. Yatay olanlar top ve bottom, dikey olanlar ise start ve end'dir.

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)
}

Bir yönerge oluşturmak için createGuidelineFrom* ile gerekli yönerge türünü kullanın. Bu, Modifier.constrainAs() bloğunda kullanılabilecek bir referans oluşturur.

Bariyerler

Barriers, belirtilen taraftaki en uç widget'a dayalı sanal bir kılavuz çizgisi oluşturmak için birden fazla composable'a referans verir.

Bariyer oluşturmak için createTopBarrier() (veya: createBottomBarrier(), createEndBarrier(), createStartBarrier()) simgesini kullanın ve bariyeri oluşturacak referansları girin.

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

        val topBarrier = createTopBarrier(button, text)
    }
}

Daha sonra bariyer, Modifier.constrainAs() bloğunda kullanılabilir.

Zincir

Zincirler, tek bir eksende (yatay veya dikey) gruplara benzer davranışlar sağlar. Diğer eksen bağımsız olarak kısıtlanabilir.

Zincir oluşturmak için createVerticalChain veya createHorizontalChain simgesini kullanın:

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)
    }
}

Daha sonra zincir, Modifier.constrainAs() bloğunda kullanılabilir.

Bir zincir, farklı ChainStyles ile yapılandırılabilir. Bu ChainStyles, bir composable'ı çevreleyen alanla nasıl ilgilenileceğini belirler. Örneğin:

  • ChainStyle.Spread: Boşluk, ilk composable'dan önceki ve son composable'dan sonraki boş alan da dahil olmak üzere tüm composable'lar arasında eşit olarak dağıtılır.
  • ChainStyle.SpreadInside: Boşluk, tüm composable'lar arasında eşit olarak dağıtılır. İlk composable'dan önce veya son composable'dan sonra boş alan olmaz.
  • ChainStyle.Packed: Boşluk, ilk composable'dan önce ve son composable'dan sonra dağıtılır. Composables, aralarında boşluk olmadan birbirine yakın şekilde yerleştirilir.