Benutzerdefinierte Layouts

In Compose werden UI-Elemente durch die zusammensetzbaren Funktionen dargestellt, die beim Aufrufen eine UI ausgeben. Diese wird dann einem UI-Baum hinzugefügt, der auf dem Bildschirm gerendert wird. Jedes UI-Element hat ein übergeordnetes Element und potenziell viele untergeordnete Elemente. Jedes Element befindet sich auch in seinem übergeordneten Element, angegeben als (x, y)-Position und Größe, angegeben als width und height.

Übergeordnete Elemente definieren die Einschränkungen für ihre untergeordneten Elemente. Die Größe eines Elements muss innerhalb dieser Einschränkungen definiert werden. Mit Einschränkungen werden die Mindest- und Höchstwerte width und height eines Elements begrenzt. Wenn ein Element untergeordnete Elemente hat, wird möglicherweise jedes untergeordnete Element gemessen, um die Größe zu bestimmen. Sobald ein Element seine eigene Größe bestimmt und meldet, kann es festlegen, wie seine untergeordneten Elemente relativ zu sich selbst platziert werden sollen. Dies wird unter Benutzerdefinierte Layouts erstellen ausführlich beschrieben.

Die Anordnung der einzelnen Knoten im UI-Baum erfolgt in drei Schritten. Jeder Knoten muss:

  1. Untergeordnete Elemente messen
  2. Eigene Größe festlegen
  3. Untergeordnete Elemente platzieren

Drei Schritte für das Knotenlayout: Kinder messen, Größe festlegen, Kinder platzieren

Mithilfe von Bereichen wird festgelegt, wann Sie Ihre untergeordneten Elemente messen und platzieren können. Ein Layout kann nur während der Mess- und Layoutdurchläufe gemessen werden. Ein untergeordnetes Element kann nur während der Layoutdurchläufe platziert werden (und nur, nachdem es gemessen wurde). Aufgrund von Compose-Bereichen wie MeasureScope und PlacementScope wird dies zur Kompilierungszeit erzwungen.

Layout-Modifikator verwenden

Mit dem Modifikator layout können Sie ändern, wie ein Element gemessen und angeordnet wird. Layout ist ein Lambda; seine Parameter umfassen das Element, das Sie messen können, übergeben als measurable, und die eingehenden Einschränkungen dieses Composeables, übergeben als constraints. Ein benutzerdefinierter Layout-Modifikator kann so aussehen:

fun Modifier.customLayoutModifier() =
    layout { measurable, constraints ->
        // ...
    }

Zeigen wir ein Text auf dem Bildschirm an und steuern den Abstand von der Oberkante zur Grundlinie der ersten Textzeile. Genau das macht der Modifikator paddingFromBaseline. Wir implementieren ihn hier als Beispiel. Dazu platzieren Sie das Composeable mit der Tastenkombination layout manuell auf dem Bildschirm. Hier ist das gewünschte Verhalten, wenn das obere Text-Abstand 24.dp festgelegt ist:

Zeigt den Unterschied zwischen dem normalen UI-Padding, das den Abstand zwischen den Elementen festlegt, und dem Text-Padding, durch den der Abstand von einer Referenz zur nächsten festgelegt wird

Hier ist der Code, mit dem dieses Spacing erzeugt wird:

fun Modifier.firstBaselineToTop(
    firstBaselineToTop: Dp
) = layout { measurable, constraints ->
    // Measure the composable
    val placeable = measurable.measure(constraints)

    // Check the composable has a first baseline
    check(placeable[FirstBaseline] != AlignmentLine.Unspecified)
    val firstBaseline = placeable[FirstBaseline]

    // Height of the composable with padding - first baseline
    val placeableY = firstBaselineToTop.roundToPx() - firstBaseline
    val height = placeable.height + placeableY
    layout(placeable.width, height) {
        // Where the composable gets placed
        placeable.placeRelative(0, placeableY)
    }
}

Hier ist, was in diesem Code passiert:

  1. Im Lambda-Parameter measurable messen Sie den Text, der durch den messbaren Parameter dargestellt wird, indem Sie measurable.measure(constraints) aufrufen.
  2. Sie geben die Größe des Composeables an, indem Sie die Methode layout(width, height) aufrufen. Dadurch wird auch ein Lambda-Ausdruck zurückgegeben, der zum Platzieren der umhüllten Elemente verwendet wird. In diesem Fall ist dies die Höhe zwischen der letzten Referenz und dem hinzugefügten oberen Abstand.
  3. Positionieren Sie die umschlossenen Elemente auf dem Bildschirm, indem Sie placeable.place(x, y) aufrufen. Wenn die umhüllten Elemente nicht platziert werden, sind sie nicht sichtbar. Die y-Position entspricht dem oberen Abstand, also der Position der ersten Textgrundlinie.

Verwenden Sie diesen Modifikator für Text, um zu prüfen, ob dies wie erwartet funktioniert:

@Preview
@Composable
fun TextWithPaddingToBaselinePreview() {
    MyApplicationTheme {
        Text("Hi there!", Modifier.firstBaselineToTop(32.dp))
    }
}

@Preview
@Composable
fun TextWithNormalPaddingPreview() {
    MyApplicationTheme {
        Text("Hi there!", Modifier.padding(top = 32.dp))
    }
}

Mehrere Vorschaubilder von Textelementen; eines zeigt den normalen Abstand zwischen den Elementen, das andere den Abstand von einer Grundlinie zur nächsten

Benutzerdefinierte Layouts erstellen

Der Modifizierer layout ändert nur das Anruf-Composeable. Wenn Sie mehrere Composeables messen und layouten möchten, verwenden Sie stattdessen das Composeable Layout. Mit dieser zusammensetzbaren Funktion können Sie Kinder manuell messen und anordnen. Alle Layouts der höheren Ebenen wie Column und Row werden mit dem Layout-Kompositelement erstellt.

Erstellen wir eine sehr einfache Version von Column. Die meisten benutzerdefinierten Layouts folgen diesem Muster:

@Composable
fun MyBasicColumn(
    modifier: Modifier = Modifier,
    content: @Composable () -> Unit
) {
    Layout(
        modifier = modifier,
        content = content
    ) { measurables, constraints ->
        // measure and position children given constraints logic here
        // ...
    }
}

Ähnlich wie beim Modifikator layout ist measurables die Liste der untergeordneten Elemente, die gemessen werden sollen, und constraints sind die Einschränkungen des übergeordneten Elements. Gemäß der gleichen Logik wie zuvor kann MyBasicColumn so implementiert werden:

@Composable
fun MyBasicColumn(
    modifier: Modifier = Modifier,
    content: @Composable () -> Unit
) {
    Layout(
        modifier = modifier,
        content = content
    ) { measurables, constraints ->
        // Don't constrain child views further, measure them with given constraints
        // List of measured children
        val placeables = measurables.map { measurable ->
            // Measure each children
            measurable.measure(constraints)
        }

        // Set the size of the layout as big as it can
        layout(constraints.maxWidth, constraints.maxHeight) {
            // Track the y co-ord we have placed children up to
            var yPosition = 0

            // Place children in the parent layout
            placeables.forEach { placeable ->
                // Position item on the screen
                placeable.placeRelative(x = 0, y = yPosition)

                // Record the y co-ord placed up to
                yPosition += placeable.height
            }
        }
    }
}

Die untergeordneten Composeables sind durch die Layout-Einschränkungen (ohne die minHeight-Einschränkungen) eingeschränkt und werden basierend auf der yPosition des vorherigen Composeables platziert.

So wird dieses benutzerdefinierte Composed-Element verwendet:

@Composable
fun CallingComposable(modifier: Modifier = Modifier) {
    MyBasicColumn(modifier.padding(8.dp)) {
        Text("MyBasicColumn")
        Text("places items")
        Text("vertically.")
        Text("We've done it by hand!")
    }
}

Mehrere Textelemente, die in einer Spalte übereinander gestapelt sind.

Layout-Richtung

Ändern Sie die Layoutrichtung eines Composeables, indem Sie die lokale LocalLayoutDirection-Komposition ändern.

Wenn Sie zusammensetzbare Funktionen manuell auf dem Bildschirm platzieren, ist das LayoutDirection Teil der LayoutScope des layout-Modifikator oder der zusammensetzbaren Layout.

Wenn Sie layoutDirection verwenden, platzieren Sie die zusammensetzbaren Funktionen mit place. Im Gegensatz zur Methode placeRelative ändert sich place nicht abhängig von der Layoutrichtung (von links nach rechts oder von rechts nach links).

Benutzerdefinierte Layouts in der Praxis

Weitere Informationen zu Layouts und Modifikatoren finden Sie unter Grundlegende Layouts in Compose. In den Beispielen für benutzerdefinierte Layouts in Compose können Sie sich benutzerdefinierte Layouts in Aktion ansehen.

Weitere Informationen

Weitere Informationen zu benutzerdefinierten Layouts in Compose finden Sie in den folgenden zusätzlichen Ressourcen.

Videos