Układ ograniczeń w tworzeniu

ConstraintLayout to układ, który umożliwia umieszczanie komponentów kompozycyjnych względem innych komponentów kompozycyjnych na ekranie. Jest to alternatywa dla używania wielu zagnieżdżonych elementów Row, Column, Box i innych elementów układu niestandardowego. ConstraintLayout przydaje się podczas wdrażania większych układów o bardziej skomplikowanych wymaganiach dotyczących wyrównania.

Używaj ConstraintLayout w tych sytuacjach:

  • Aby uniknąć zagnieżdżania wielu elementów ColumnRow w celu pozycjonowania elementów na ekranie i poprawić czytelność kodu.
  • Aby pozycjonować komponenty kompozycyjne względem innych komponentów kompozycyjnych lub pozycjonować komponenty kompozycyjne na podstawie wytycznych, barier lub łańcuchów.

W systemie View zalecanym sposobem tworzenia dużych i złożonych układów było używanie elementu ConstraintLayout, ponieważ płaska hierarchia widoków zapewniała lepszą wydajność niż widoki zagnieżdżone. Nie jest to jednak problem w Compose, który może efektywnie obsługiwać głębokie hierarchie układów.

Rozpocznij korzystanie z usługi ConstraintLayout

Aby używać ConstraintLayout w Compose, musisz dodać tę zależność w pliku build.gradle (oprócz konfiguracji Compose):

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

ConstraintLayout w Compose działa w ten sposób, korzystając z DSL:

  • Utwórz odwołania do każdego elementu kompozycyjnego w ConstraintLayout za pomocą createRefs() lub createRefFor().
  • Ograniczenia są podawane za pomocą modyfikatora constrainAs(), który przyjmuje odwołanie jako parametr i umożliwia określenie jego ograniczeń w treści lambda.
  • Ograniczenia są określane za pomocą linkTo() lub innych przydatnych metod.
  • parent to istniejące odwołanie, którego można użyć do określenia ograniczeń w stosunku do samego elementu kompozycyjnego ConstraintLayout.

Oto przykład funkcji kompozycyjnej korzystającej z funkcji 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)
            }
        )
    }
}

Ten kod ogranicza górną krawędź elementu Button do elementu nadrzędnego z marginesem 16.dp, a dolną krawędź elementu Button do dolnej krawędzi elementu nadrzędnego również z marginesem 16.dp.Text

Wyświetla przycisk i element tekstowy ułożone w obiekcie ConstraintLayout

Oddzielony interfejs API

W przykładzie ConstraintLayout ograniczenia są określone w wierszu, a modyfikator znajduje się w funkcji kompozycyjnej, do której są stosowane. Czasami jednak lepiej jest oddzielić ograniczenia od układów, do których się odnoszą. Możesz na przykład zmienić ograniczenia na podstawie konfiguracji ekranu lub animować przejście między 2 zestawami ograniczeń.

W takich przypadkach możesz użyć symbolu ConstraintLayout w inny sposób:

  1. Przekaż ConstraintSet jako parametr do ConstraintLayout.
  2. Przypisz odwołania utworzone w ConstraintSet do funkcji kompozycyjnych za pomocą modyfikatora 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)
        }
    }
}

Gdy zechcesz zmienić ograniczenia, wystarczy, że przekażesz inne ConstraintSet.

ConstraintLayout pojęć

ConstraintLayout zawiera koncepcje takie jak wytyczne, bariery i łańcuchy, które mogą pomóc w umieszczaniu elementów w kompozycji.

Wskazówki

Linie pomocnicze to niewielkie elementy wizualne, które ułatwiają projektowanie układów. Kompozycje można ograniczyć do linii pomocniczej. Wytyczne są przydatne do pozycjonowania elementów w określonym dp lub percentage w kompozycji nadrzędnej.

Istnieją 2 rodzaje linii pomocniczych: pionowe i poziome. Dwie poziome to topbottom, a dwie pionowe to startend.

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

Aby utworzyć wytyczne, użyj polecenia createGuidelineFrom* z wymaganym typem wytycznych. Spowoduje to utworzenie odwołania, którego można użyć w bloku Modifier.constrainAs().

Bariery

Barriers odwołują się do wielu komponentów kompozycyjnych, aby utworzyć wirtualną linię pomocniczą na podstawie najbardziej wysuniętego widżetu po określonej stronie.

Aby utworzyć barierę, użyj symbolu createTopBarrier() (lub createBottomBarrier(), createEndBarrier(), createStartBarrier()) i podaj odniesienia, które mają się na nią składać.

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

        val topBarrier = createTopBarrier(button, text)
    }
}

Barierę można następnie wykorzystać w bloku Modifier.constrainAs().

Łańcuchy

Łańcuchy zapewniają zachowanie podobne do grup w przypadku jednej osi (poziomej lub pionowej). Drugą oś można ograniczyć niezależnie.

Aby utworzyć łańcuch, użyj createVerticalChain lub 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)
    }
}

Łańcuch można następnie wykorzystać w bloku Modifier.constrainAs().

Łańcuch można skonfigurować za pomocą różnych ChainStyles, które określają sposób postępowania z przestrzenią otaczającą element kompozycyjny, np.:

  • ChainStyle.Spread: przestrzeń jest równomiernie rozłożona na wszystkie komponenty, w tym wolną przestrzeń przed pierwszym komponentem i po ostatnim.
  • ChainStyle.SpreadInside: przestrzeń jest rozdzielana równomiernie między wszystkie komponenty kompozycyjne, bez wolnej przestrzeni przed pierwszym komponentem kompozycyjnym ani po ostatnim.
  • ChainStyle.Packed: przestrzeń jest rozdzielana przed pierwszym i po ostatnim komponencie, a komponenty są umieszczane obok siebie bez spacji między nimi.

Więcej informacji

Więcej informacji o ConstraintLayout w Compose znajdziesz w sekcji Interfejsy API w działaniu w przykładach Compose, które używają ConstraintLayout.