ConstraintLayout in Compose

ConstraintLayout ist ein Layout, mit dem Sie zusammensetzbare Funktionen relativ zu anderen zusammensetzbaren Funktionen auf dem Bildschirm platzieren können. Es ist eine Alternative zur Verwendung mehrerer verschachtelter Row-, Column-, Box- und anderer benutzerdefinierter Layoutelemente. ConstraintLayout ist nützlich bei der Implementierung größerer Layouts mit komplizierteren Ausrichtungsanforderungen.

Erwägen Sie die Verwendung von ConstraintLayout in den folgenden Szenarien:

  • Um zu vermeiden, dass mehrere Columns und Rows für die Positionierung von Elementen auf dem Bildschirm verschachtelt werden, um die Lesbarkeit des Codes zu verbessern.
  • Positionierung von zusammensetzbaren Funktionen im Vergleich zu anderen zusammensetzbaren Funktionen oder Positionierung von Zusammensetzbaren anhand von Richtlinien, Barrieren oder Ketten

Im Ansichtssystem war ConstraintLayout die empfohlene Methode zum Erstellen großer und komplexer Layouts, da eine flache Ansichtshierarchie im Hinblick auf die Leistung besser ist als verschachtelte Ansichten. Bei Compose ist dies jedoch kein Problem, da tiefe Layouthierarchien effizient verarbeitet werden können.

Einführung in ConstraintLayout

Wenn Sie ConstraintLayout in Compose verwenden möchten, müssen Sie diese Abhängigkeit in Ihre build.gradle einfügen (zusätzlich zur Compose-Einrichtung):

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

So funktioniert ConstraintLayout in Compose mithilfe einer DSL:

  • Erstellen Sie mit createRefs() oder createRefFor() Referenzen für jede zusammensetzbare Funktion in ConstraintLayout.
  • Einschränkungen werden mit dem constrainAs()-Modifikator bereitgestellt, der die Referenz als Parameter verwendet und es Ihnen ermöglicht, die Einschränkungen im Text-Lambda anzugeben.
  • Einschränkungen werden mithilfe von linkTo() oder anderen hilfreichen Methoden angegeben.
  • parent ist ein vorhandener Verweis, mit dem Einschränkungen gegenüber der zusammensetzbaren Funktion ConstraintLayout selbst angegeben werden können.

Hier ein Beispiel für eine zusammensetzbare Funktion mit einem 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)
            }
        )
    }
}

Mit diesem Code wird der obere Bereich von Button auf das übergeordnete Element mit einem Rand von 16.dp und einen Wert für Text auf den unteren Rand von Button beschränkt, ebenfalls mit einem Rand von 16.dp.

Zeigt eine Schaltfläche und ein Textelement in einem ConstraintLayout-Element an

Entkoppelte API

Im Beispiel ConstraintLayout werden Einschränkungen inline mit einem Modifikator in der zusammensetzbaren Funktion angegeben, auf die sie angewendet werden. Es gibt jedoch Situationen, in denen es vorteilhaft ist, die Einschränkungen von den Layouts zu entkoppeln, für die sie gelten. Beispielsweise können Sie die Einschränkungen je nach Bildschirmkonfiguration ändern oder zwischen zwei Einschränkungssätzen animieren.

Für solche Fälle können Sie ConstraintLayout anders verwenden:

  1. Übergeben Sie ConstraintSet als Parameter an ConstraintLayout.
  2. Weisen Sie mit dem Modifikator layoutId Referenzen, die in der ConstraintSet erstellt wurden, den zusammensetzbaren Funktionen zu.

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

Wenn Sie dann die Einschränkungen ändern müssen, können Sie einfach ein anderes ConstraintSet übergeben.

ConstraintLayout Konzepte

ConstraintLayout enthält Konzepte wie Richtlinien, Barrieren und Ketten, die bei der Positionierung von Elementen innerhalb der zusammensetzbaren Funktion hilfreich sind.

Vorgaben

Richtlinien sind kleine visuelle Hilfsmittel beim Entwerfen von Layouts. Composables können auf eine Richtlinie beschränkt werden. Richtlinien sind hilfreich, um Elemente an einem bestimmten dp oder percentage innerhalb der übergeordneten zusammensetzbaren Funktion zu positionieren.

Es gibt zwei verschiedene Arten von Richtlinien: vertikal und horizontal. Die beiden horizontalen sind top und bottom, die beiden vertikalen start und end.

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

Verwenden Sie zum Erstellen einer Richtlinie createGuidelineFrom* mit dem erforderlichen Typ der Richtlinie. Dadurch wird ein Verweis erstellt, der im Block Modifier.constrainAs() verwendet werden kann.

Barrieren

Hindernisse verweisen auf mehrere zusammensetzbare Funktionen, um eine virtuelle Richtlinie zu erstellen, die auf dem extremsten Widget auf der angegebenen Seite basiert.

Verwende zum Erstellen einer Hürde createTopBarrier() (oder createBottomBarrier(), createEndBarrier(), createStartBarrier()) und gib die Referenzen an, die die Hürde ausmachen sollen.

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

        val topBarrier = createTopBarrier(button, text)
    }
}

Die Hürde kann dann in einem Modifier.constrainAs()-Block verwendet werden.

Ketten

Ketten bieten ein gruppenähnliches Verhalten auf einer einzigen Achse (horizontal oder vertikal). Die andere Achse kann unabhängig voneinander fixiert werden.

Verwenden Sie zum Erstellen einer Kette entweder createVerticalChain oder 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)
    }
}

Die Kette kann dann im Modifier.constrainAs()-Block verwendet werden.

Eine Kette kann mit verschiedenen ChainStyles konfiguriert werden, die entscheiden, wie mit dem Raum umgegangen wird, der eine zusammensetzbare Funktion umgibt. Beispiele:

  • ChainStyle.Spread: Der Raum wird gleichmäßig auf alle zusammensetzbaren Funktionen verteilt, einschließlich des kostenlosen Platzes vor der ersten zusammensetzbaren Funktion und nach der letzten zusammensetzbaren Funktion.
  • ChainStyle.SpreadInside: Der Raum wird gleichmäßig auf alle zusammensetzbaren Funktionen verteilt, ohne dass vor der ersten zusammensetzbaren Funktion oder nach der letzten zusammensetzbaren Funktion Platz hat.
  • ChainStyle.Packed: Der Raum wird vor der ersten zusammensetzbaren Funktion und nach der letzten zusammensetzbaren Funktion verteilt. Die zusammensetzbaren Funktionen werden ohne Platz dazwischen zusammengefasst.

Weitere Informationen

Weitere Informationen zu ConstraintLayout in „Compose from the APIs“ in Aktion finden Sie unter Beispiele erstellen, die ConstraintLayout verwenden.