ConstraintLayout in Compose

Mit dem Layout ConstraintLayout können Sie zusammensetzbare Funktionen relativ zu anderen auf dem Bildschirm platzieren. Dies ist eine Alternative zu mehreren verschachtelten Row-, Column-, Box- und anderen benutzerdefinierten Layoutelementen. ConstraintLayout ist nützlich, wenn größere Layouts mit komplexeren Ausrichtungsanforderungen implementiert werden sollen.

Sie können ConstraintLayout in den folgenden Szenarien verwenden:

  • Das Verschachteln mehrerer Column- und Row-Elemente zur Positionierung von Elementen auf dem Bildschirm wird vermieden, um die Lesbarkeit des Codes zu verbessern.
  • Zum Positionieren von zusammensetzbaren Funktionen im Verhältnis zu anderen zusammensetzbaren Funktionen oder anhand von Richtlinien, Hindernissen oder Ketten.

Im View-System wurde ConstraintLayout die empfohlene Methode zum Erstellen großer und komplexer Layouts empfohlen, da eine flache Ansichtshierarchie für die Leistung besser war als verschachtelte Ansichten. Dies ist jedoch kein Problem in Compose, da tiefe Layouthierarchien effizient verwaltet werden können.

Einführung in ConstraintLayout

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

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

ConstraintLayout in Compose funktioniert mit einer DSL so:

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

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

Dieser Code schränkt den oberen Rand des Button-Elements auf das übergeordnete Element mit einem Rand von 16.dp und einem Text-Element am unteren Rand des Button-Elements mit einem Rand von 16.dp ein.

Zeigt eine Schaltfläche und ein Textelement, die in einem ConstraintLayout angeordnet sind

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 besser ist, die Einschränkungen von den Layouts zu entkoppeln, für die sie gelten. Sie können beispielsweise die Einschränkungen basierend auf der Bildschirmkonfiguration ändern oder zwischen zwei Einschränkungssätzen animieren.

In solchen Fällen können Sie ConstraintLayout anders verwenden:

  1. Übergeben Sie ConstraintSet als Parameter an ConstraintLayout.
  2. Weisen Sie Referenzen, die in ConstraintSet erstellt wurden, zusammensetzbaren Funktionen mit dem Modifikator layoutId 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 eine andere ConstraintSet übergeben.

ConstraintLayout-Konzepte

ConstraintLayout enthält Konzepte wie Richtlinien, Barrieren und Ketten, die beim Positionieren von Elementen in der zusammensetzbaren Funktion helfen können.

Vorgaben

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

Es gibt zwei verschiedene Arten von Richtlinien: vertikale und horizontale Richtlinien. Die beiden horizontalen sind top und bottom, die beiden vertikal sind 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 der erforderlichen Art der Richtlinie. Dadurch wird ein Verweis erstellt, der im Modifier.constrainAs()-Block verwendet werden kann.

Barrieren

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

Verwenden Sie zum Erstellen einer Hürde createTopBarrier() (oder: createBottomBarrier(), createEndBarrier(), createStartBarrier()) und geben Sie die Referenzen an, die die Hürde bilden sollen.

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

        val topBarrier = createTopBarrier(button, text)
    }
}

Die Barriere kann dann in einem Modifier.constrainAs()-Block verwendet werden.

Ketten

Ketten bieten ein gruppenähnliches Verhalten auf einer einzelnen Achse (horizontal oder vertikal). Die andere Achse kann unabhängig 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 bestimmen, wie mit dem Bereich um eine zusammensetzbare Funktion umgegangen wird:

  • ChainStyle.Spread: Der Bereich wird gleichmäßig auf alle zusammensetzbaren Funktionen verteilt, einschließlich des kostenlosen Bereichs vor der ersten und nach der letzten zusammensetzbaren Funktion.
  • ChainStyle.SpreadInside: Der Bereich wird gleichmäßig auf alle zusammensetzbaren Funktionen verteilt, ohne dass vor der ersten zusammensetzbaren Funktion oder nach der letzten zusammensetzbaren Funktion kostenloser Bereich vorhanden ist.
  • ChainStyle.Packed: Der Bereich wird vor der ersten und nach der letzten zusammensetzbaren Funktion verteilt. Dabei werden sie ohne Leerzeichen zwischeneinander gepackt.

Weitere Informationen

Weitere Informationen zu ConstraintLayout in Compose aus den APIs in Aktion finden Sie in den Compose-Beispielen mit ConstraintLayout.