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 użycia wielu zagnieżdżonych elementów Row, Column, Boxinnych elementów niestandardowych układu. ConstraintLayoutjest przydatna 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 względem innych elementów lub elementów 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 „Compose” działa w następujący sposób za pomocą języka DSL:

  • 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 oraz elementu Text do dolnej krawędzi elementu Button również z marginesem 16.dp.

Ilustracja pokazująca 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 chcesz zmienić ograniczenia, możesz przekazać 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 używają interfejsu ConstraintLayout.