Compose'da ConstraintLayout

ConstraintLayout, composable'ları ekrandaki diğer composable'lara göre yerleştirebilmenizi sağlayan bir düzendir. İç içe yerleştirilmiş birden fazla Row, Column, Box ve diğer özel düzen öğelerini kullanmaya alternatiftir. ConstraintLayout, daha karmaşık eşleşme gereksinimleri olan daha büyük düzenler uygularken kullanışlıdır.

Aşağıdaki senaryolarda ConstraintLayout kullanma seçeneğini değerlendirin:

  • Kodun okunabilirliğini iyileştirmek amacıyla, öğeleri ekrandaki konumlandırmak için birden fazla Column ve Row'ın iç içe yerleştirilmesini önlemek amaçlanır.
  • composable'ları diğer composable'lara göre veya kurallara, bariyerlere ya da zincirlere göre konumlandırmak için kullanılır.

Performans açısından iç içe yerleştirilmiş görünümlere kıyasla düz görünüm hiyerarşisi daha iyi olduğundan, View sisteminde büyük ve karmaşık düzenler oluşturmak için ConstraintLayout önerilen yoldu. Bununla birlikte, derin düzen hiyerarşilerini verimli bir şekilde yönetebilen Compose için bu durum bir sorun teşkil etmez.

ConstraintLayout hizmetini kullanmaya başla

Compose'da ConstraintLayout kullanmak için bu bağımlılığı build.gradle öğelerinize eklemeniz gerekir (Oluşturma ayarına ek olarak):

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

Compose'da ConstraintLayout DSL kullanıldığında şu şekilde çalışır:

  • createRefs() veya createRefFor() kullanarak ConstraintLayout içinde her composable için referans oluşturun
  • Sınırlamalar, referansı bir parametre olarak alan ve body lambda'da referansın kısıtlamalarını belirtebilmenizi sağlayan constrainAs() değiştiricisi kullanılarak sağlanır.
  • Kısıtlamalar linkTo() veya diğer yardımcı yöntemler kullanılarak belirtilir.
  • parent, ConstraintLayout composable'ına yönelik kısıtlamalar belirtmek için kullanılabilecek mevcut bir referanstır.

ConstraintLayout kullanılan 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ğu ile üst öğeye, Button alt kısmı için de Text 16.dp kenar boşluğu ile sınırlar.

ConstraintLayout biçiminde düzenlenmiş bir düğme ve metin öğesi gösterir

Ayrılmış API

ConstraintLayout örneğinde, kısıtlamalar satır içinde belirtilir ve uygulandıkları composable'da bir değiştirici bulunur. Bununla birlikte, kısıtlamaların geçerli oldukları düzenlerden ayrılmasının tercih edildiği durumlar vardır. Örneğin, ekran yapılandırmasına göre kısıtlamaları değiştirmek veya iki sınırlama grubu arasında animasyon uygulamak isteyebilirsiniz.

Bu gibi durumlarda, ConstraintLayout öğesini farklı bir şekilde kullanabilirsiniz:

  1. ConstraintLayout öğesine parametre olarak bir ConstraintSet iletin.
  2. ConstraintSet özelliğ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 kavram

ConstraintLayout, Composable içindeki öğeleri konumlandırmanıza yardımcı olabilecek yönergeler, bariyerler ve zincirler gibi kavramlar içerir.

Kurallar

Yönergeler, düzenleri tasarlamada kullanılacak küçük görsel yardımcılardır. Composable'lar bir kuralla sınırlandırılabilir. Yönergeler, öğeleri üst composable'ın içinde belirli bir dp veya percentage konumuna yerleştirmek için faydalıdır.

Dikey ve yatay olmak üzere iki farklı kural türü vardır. Yatay olan top ve bottom, iki dikey ise start ve end şeklindedir.

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

Kılavuz oluşturmak için gereken yönerge türüyle createGuidelineFrom* öğesini kullanın. Bu işlem, Modifier.constrainAs() bloğunda kullanılabilecek bir referans oluşturur.

Bariyerler

Bariyerler, belirtilen taraftaki en uç widget'a dayanarak sanal bir kural oluşturmak için birden fazla composable'a referans verir.

Bariyer oluşturmak için createTopBarrier() (veya createBottomBarrier(), createEndBarrier(), createStartBarrier()) kullanın ve engeli oluşturması gereken referansları sağlayın.

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

        val topBarrier = createTopBarrier(button, text)
    }
}

Bariyer daha sonra Modifier.constrainAs() blokunda kullanılabilir.

Zincirler

Zincirler, tek bir eksende (yatay veya dikey) grup benzeri davranışlar sağlar. Diğer eksen bağımsız olarak sınırlanabilir.

Zincir oluşturmak için createVerticalChain veya createHorizontalChain 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)
    }
}

Bu zincir, Modifier.constrainAs() blokunda kullanılabilir.

Zincir, bir composable'ı çevreleyen alanın nasıl ele alınacağını belirleyen farklı ChainStyles ile yapılandırılabilir. Örneğin:

  • ChainStyle.Spread: Alan, tüm composable'lara eşit olarak dağıtılır. Bu sırada ilk composable'dan önceki ve sondaki composable'dan sonraki boş alan da dahildir.
  • ChainStyle.SpreadInside: Alan, ilk composable'dan önce veya son composable'dan sonra boş alan olmadan tüm composable'lara eşit olarak dağıtılır.
  • ChainStyle.Packed: Alan, ilk composable'dan önce ve son composable'dan sonra dağıtılır. composable'lar, aralarında boşluk olmadan bir araya getirilir.

Daha fazla bilgi

Compose'daki API'lerden yararlanarak ConstraintLayout kullanan örnekler oluşturma bölümünde ConstraintLayout hakkında daha fazla bilgi edinin.