Układ ograniczeń w tworzeniu

ConstraintLayout to układ, który umożliwia umieszczanie komponentów względem innych komponentów na ekranie. Jest to alternatywa dla korzystania z wielu zagnieżdżonych elementów Row, Column, Boxinnych elementów niestandardowych układu. ConstraintLayoutprzydaje się podczas wdrażania większych układów o bardziej skomplikowanych wymaganiach dotyczących wyrównania;

Możesz użyć ConstraintLayout w tych sytuacjach:

  • Aby uniknąć zagnieżdżania elementów ColumnRow w celu pozycjonowania elementów na ekranie i poprawienia czytelności kodu.
  • Pozycjonowanie elementów składanych względem innych elementów składanych lub pozycjonowanie elementów składanych na podstawie linii pomocniczych, barier lub łańcuchów.

W systemie widoków widok ConstraintLayout był zalecanym sposobem tworzenia dużych i złożonych układów, ponieważ płaska hierarchia widoków zapewniała lepszą wydajność niż widoki zagnieżdżone. W Compose nie ma to jednak znaczenia, ponieważ to narzędzie może skutecznie obsługiwać głębokie hierarchie układu.

Rozpocznij korzystanie z usługi ConstraintLayout

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

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

ConstraintLayout w sekcji Komponuj działa w następujący sposób za pomocą języka:

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

Oto przykład kompozytu używającego element 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 element Text do dolnej krawędzi elementu Button również z marginesem 16.dp.

Przycisk i element tekstowy uporządkowane w ConstraintLayout

Odłączone interfejsy API

W przykładzie ConstraintLayout ograniczenia są określone w tekście, a modyfikator znajduje się w składanym elemencie, do którego są stosowane. Są jednak sytuacje, w których lepiej jest odłączyć ograniczenia od układów, do których się odnoszą. Możesz np. zmienić ograniczenia na podstawie konfiguracji ekranu lub animować przejście między 2 zestawami ograniczeń.

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

  1. Jako parametr funkcji ConstraintLayout prześlij wartość ConstraintSet.
  2. Przypisz odwołania utworzone w komponencie ConstraintSet do komponentów 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 trzeba zmienić ograniczenia, możesz po prostu podać inną wartość parametru ConstraintSet.

ConstraintLayout pojęcia

ConstraintLayout zawiera takie elementy jak linie pomocnicze, bariery i łańcuchy, które mogą pomóc w pozycjonowaniu elementów w ramach komponentu.

Wskazówki

Wskazówki to małe wizualne pomoce ułatwiające projektowanie układów. Elementy składane mogą być ograniczone do jednej wskazówki. Wytyczne są przydatne do pozycjonowania elementów w określonym dp lub percentage w ramach komponentu nadrzędnego.

Istnieją 2 rodzaje wytycznych: pionowe i poziome. 2 poziome: topbottom, 2 pionowe: 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 instrukcji createGuidelineFrom* z odpowiednim typem wytycznych. W ten sposób utworzysz odwołanie, którego możesz użyć w bloku Modifier.constrainAs().

Bariery

Bariery odwołują się do wielu komponentów, aby utworzyć wirtualną linię orientacyjną na podstawie najbardziej skrajnych danych w wybranym obszarze.

Aby utworzyć barierę, użyj elementu createTopBarrier() (lub createBottomBarrier(), createEndBarrier(), createStartBarrier()) i podaj pliki referencyjne, które mają ją tworzyć.

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ą działanie podobne do działania grup w jednej osi (poziomej lub pionowej). Druga oś może być ograniczona 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 użyć w bloku Modifier.constrainAs().

Łańcuch można skonfigurować za pomocą różnych ChainStyles, które określają sposób obsługi przestrzeni otaczającej kompozyt, np.:

  • ChainStyle.Spread: przestrzeń jest rozłożona równomiernie na wszystkie elementy, w tym wolna przestrzeń przed pierwszym elementem i po ostatnim.
  • ChainStyle.SpreadInside: przestrzeń jest rozłożona równomiernie na wszystkie elementy, bez wolnej przestrzeni przed pierwszym elementem i po ostatnim.
  • ChainStyle.Packed: spacja jest rozmieszczana przed pierwszym i po ostatnim składanym, a składane są umieszczone obok siebie bez odstępów.

Więcej informacji

Dowiedz się więcej o interfejsie ConstraintLayout w Compose, korzystając z interfejsów API w praktyce w przykładach Compose, które korzystają z interfejsu ConstraintLayout.