Compose'da ConstraintLayout

ConstraintLayout, composable'ları ekrandaki diğer composable'lara göre yerleştirmenize olanak tanıyan bir düzendir. Bu özellik, iç içe yerleştirilmiş birden fazla Row, Column, Box ve diğer özel düzen öğelerini kullanmaya alternatif bir yöntemdir. ConstraintLayout Daha karmaşık hizalama gereksinimlerine sahip daha büyük düzenlemeler uygularken kullanışlıdır.

Aşağıdaki senaryolarda ConstraintLayout kullanmayı düşünebilirsiniz:

  • Kodu daha okunabilir hale getirmek için öğeleri ekranda konumlandırırken birden fazla Column ve Row iç içe yerleştirmekten kaçının.
  • Composable'ları diğer composable'lara göre veya kılavuzlara, bariyerlere ya da zincirlere göre konumlandırmak için kullanılır.

View sisteminde, düz görünüm hiyerarşisi iç içe 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.

ConstraintLayout hizmetini kullanmaya başla

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

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

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

  • ConstraintLayout içinde her birleştirilebilir için createRefs() veya createRefFor() kullanarak referans oluşturun.
  • Kısıtlamalar, referansı parametre olarak alan ve kısıtlamalarını lambda gövdesinde 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.

ConstraintLayout'ta düzenlenmiş bir düğme ve metin öğesi gösterir.

Ayrılmış API

ConstraintLayout örneğinde kısıtlamalar, uygulandıkları composable'da bir değiştiriciyle birlikte satır içi olarak belirtilir. Ancak, kısıtlamaları uygulandıkları düzenlerden ayırmanın tercih edileceği durumlar da 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ılar. 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. İki yatay olan top ve bottom, iki dikey olan ise start ve end'dür.

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() blokunda 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.

Engel oluşturmak için createTopBarrier() (veya: createBottomBarrier(), createEndBarrier(), createStartBarrier()) simgesini kullanın ve engeli 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.

Zincirler

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'lara eşit şekilde 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 birleştirilebilir öğeden önce ve son birleştirilebilir öğeden sonra dağıtılır. Birleştirilebilir öğeler, aralarında boşluk olmadan birbirine yakın şekilde yerleştirilir.

Daha fazla bilgi

ConstraintLayout kullanan beste örneklerinde, Beste'de ConstraintLayout ile ilgili daha fazla bilgi edinin.