Benutzerdefinierte Layouts

In Compose werden UI-Elemente durch zusammensetzbare Funktionen dargestellt, die bei Aufruf ein UI-Element ausgeben, das dann einem UI-Baum hinzugefügt wird, der auf dem Bildschirm gerendert wird. Jedes UI-Element hat ein übergeordnetes Element und möglicherweise viele untergeordnete Elemente. Jedes Element befindet sich auch innerhalb seines übergeordneten Elements, das als (x, y)-Position und als Größe (width und height) angegeben wird.

Eltern definieren die Einschränkungen für ihre untergeordneten Elemente. Ein Element wird aufgefordert, seine Größe innerhalb dieser Einschränkungen zu definieren. Mit Einschränkungen wird der minimale und maximale width- und height-Wert eines Elements eingeschränkt. Wenn ein Element untergeordnete Elemente hat, kann es jedes untergeordnete Element messen, um seine Größe zu bestimmen. Sobald ein Element seine eigene Größe bestimmt und gemeldet hat, kann es festlegen, wie seine untergeordneten Elemente relativ zu ihm platziert werden sollen. Dies wird ausführlich unter Benutzerdefinierte Layouts erstellen beschrieben.

Das Layout jedes Knotens im UI-Baum erfolgt in drei Schritten. Jeder Knoten muss:

  1. Untergeordnete Elemente messen
  2. Größe selbst bestimmen
  3. Untergeordnete Elemente platzieren

Drei Schritte des Knotenlayouts: untergeordnete Elemente messen, Größe festlegen, untergeordnete Elemente platzieren

Die Verwendung von Bereichen definiert, wann Sie Ihre Kinder messen und platzieren können. Ein Layout kann nur während der Mess- und Layout-Durchläufe gemessen werden. Ein untergeordnetes Element kann nur während der Layout-Durchläufe platziert werden (und erst, nachdem es gemessen wurde). Aufgrund von Compose-Bereichen wie MeasureScope und PlacementScope wird dies zur Kompilierzeit erzwungen.

Layout-Modifikator verwenden

Mit dem Modifikator layout können Sie ändern, wie ein Element gemessen und angeordnet wird. Layout ist ein Lambda. Zu den Parametern gehören das messbare Element, das als measurable übergeben wird, und die eingehenden Einschränkungen des zusammensetzbaren Elements, die als constraints übergeben werden. Ein benutzerdefinierter Layoutmodifikator kann so aussehen:

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

Wir lassen ein Text auf dem Bildschirm anzeigen und steuern den Abstand vom oberen Rand zur Grundlinie der ersten Textzeile. Genau das macht der Modifikator paddingFromBaseline. Wir implementieren ihn hier als Beispiel. Verwenden Sie dazu den Modifier layout, um die Composable manuell auf dem Bildschirm zu platzieren. So sieht das gewünschte Verhalten aus, wenn der obere Innenabstand des Text auf 24.dp festgelegt ist:

Zeigt den Unterschied zwischen normalem UI-Abstand, der den Abstand zwischen Elementen festlegt, und Textabstand, der den Abstand von einer Grundlinie zur nächsten festlegt

Mit diesem Code wird der Abstand erzeugt:

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

Das passiert in diesem Code:

  1. Im Lambda-Parameter measurable wird die Text, die durch den messbaren Parameter dargestellt wird, durch Aufrufen von measurable.measure(constraints) gemessen.
  2. Sie geben die Größe des Composables an, indem Sie die Methode layout(width, height) aufrufen. Diese Methode gibt auch ein Lambda zurück, mit dem die umschlossenen Elemente platziert werden. In diesem Fall ist es die Höhe zwischen der letzten Baseline und dem hinzugefügten oberen Padding.
  3. Die umbrochenen Elemente werden auf dem Bildschirm positioniert, indem Sie placeable.place(x, y) aufrufen. Wenn die umbrochenen Elemente nicht platziert werden, sind sie nicht sichtbar. Die y-Position entspricht dem oberen Innenabstand – der Position der ersten Grundlinie des Texts.

Um zu prüfen, ob dies wie erwartet funktioniert, verwenden Sie diesen Modifikator für einen Text:

@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 Vorschauen von Textelementen: Eine zeigt den normalen Abstand zwischen Elementen, die andere den Abstand von einer Grundlinie zur nächsten.

Benutzerdefinierte Layouts erstellen

Der Modifizierer layout ändert nur die Calling-Composable. Wenn Sie mehrere Composables messen und anordnen möchten, verwenden Sie stattdessen das Composable Layout. Mit dieser Composable-Funktion können Sie untergeordnete Elemente manuell messen und anordnen. Alle Layouts auf höherer Ebene wie Column und Row werden mit der zusammensetzbaren Funktion Layout erstellt.

Wir erstellen 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 müssen, und constraints die Einschränkungen des übergeordneten Elements. Nach derselben 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 Composables werden durch die Layout-Einschränkungen (ohne die minHeight-Einschränkungen) eingeschränkt und basierend auf dem yPosition des vorherigen Composables platziert.

So wird das benutzerdefinierte Composable 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.

Layoutrichtung

Sie können die Layoutrichtung eines Composables ändern, indem Sie die lokale Komposition LocalLayoutDirection ändern.

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

Wenn Sie layoutDirection verwenden, platzieren Sie Composables mit place. Im Gegensatz zur Methode placeRelative ändert sich place nicht basierend auf 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 Einfache Layouts in Compose. Beispiele für benutzerdefinierte Layouts finden Sie in den Compose-Beispielen, in denen benutzerdefinierte Layouts erstellt werden.

Weitere Informationen

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

Videos