Układy niestandardowe

W Compose elementy interfejsu są reprezentowane przez funkcje typu „composable”, które po wywołaniu emitują fragment interfejsu. Jest on następnie dodawany do drzewa interfejsu, które jest renderowane na ekranie. Każdy element interfejsu ma 1 element nadrzędny i potencjalnie wiele elementów podrzędnych. Każdy element znajduje się też w elemencie nadrzędnym, określonym jako pozycja (x, y) i rozmiar, określony jako width i height.

Elementy nadrzędne określają ograniczenia dla swoich elementów podrzędnych. Element jest proszony o określenie swojego rozmiaru w ramach tych ograniczeń. Ograniczenia ograniczają minimalną i maksymalną width i height elementu. Jeśli element ma elementy podrzędne, może zmierzyć każdy z nich, aby określić swój rozmiar. Gdy element określi i zgłosi swój rozmiar, może zdefiniować, jak umieścić swoje elementy podrzędne względem siebie, jak opisano szczegółowo w artykule Tworzenie układów niestandardowych.

Układ każdego węzła w drzewie interfejsu to proces 3-etapowy. Każdy węzeł musi:

  1. zmierzyć wszystkie elementy podrzędne,
  2. określić swój rozmiar,
  3. umieścić elementy podrzędne.
3 etapy układu węzłów: pomiar elementów podrzędnych, określanie rozmiaru, umieszczanie elementów podrzędnych
Rysunek 1. 3 etapy układu węzła: pomiar elementów podrzędnych, określenie rozmiaru, i umieszczenie elementów podrzędnych.

Użycie zakresów określa, kiedy możesz mierzyć i umieszczać elementy podrzędne. Pomiar układu można przeprowadzić tylko podczas pomiaru i układu, a element podrzędny można umieścić tylko podczas układu (i tylko po jego zmierzeniu). Jest to wymuszane w czasie kompilacji przez zakresy Compose, takie jak MeasureScope, i PlacementScope,

Używanie modyfikatora układu

Za pomocą modyfikatora layout możesz modyfikować sposób pomiaru i układu elementu. Layout to lambda. Jej parametry obejmują element, który można zmierzyć, przekazywany jako measurable, oraz ograniczenia przychodzące tego elementu typu „composable”, przekazywane jako constraints. Niestandardowy modyfikator układu może wyglądać tak:

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

Wyświetl Text na ekranie i kontroluj odległość od góry do linii bazowej pierwszego wiersza tekstu. Dokładnie to robi modyfikator paddingFromBaseline. Implementujesz go tutaj jako przykład. Aby to zrobić, użyj modyfikatora layout, aby ręcznie umieścić element typu „composable” na ekranie. Oto wynikowe zachowanie, w którym górny margines wewnętrzny Text jest ustawiony na 24.dp:

Pokazuje różnicę między zwykłym dopełnieniem interfejsu, które określa odstęp między elementami, a dopełnieniem tekstu, które określa odstęp między kolejnymi liniami bazowymi.
Rysunek 2. Tekst z zastosowanym paddingFromBaseline.

Oto kod, który tworzy to odstępy:

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

Co się dzieje w tym kodzie:

  1. W parametrze lambda measurable mierzysz Text reprezentowany przez parametr „measurable”, wywołując measurable.measure(constraints).
  2. Określasz rozmiar elementu typu „composable”, wywołując metodę layout(width, height), która również udostępnia lambdę używaną do umieszczania opakowanych elementów. W tym przypadku jest to wysokość między ostatnią linią bazową a dodanym górnym marginesem wewnętrznym.
  3. Umieszczasz opakowane elementy na ekranie, wywołując placeable.place(x, y). Jeśli opakowane elementy nie zostaną umieszczone, nie będą widoczne. Pozycja y odpowiada górnemu marginesowi wewnętrznemu: pozycji pierwszej linii bazowej tekstu.

Aby sprawdzić, czy działa to zgodnie z oczekiwaniami, użyj tego modyfikatora w 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))
    }
}

Wiele podglądów elementów tekstowych: jeden pokazuje zwykły odstęp między elementami, a drugi odstęp od jednej linii bazowej do drugiej.
Rysunek 3. Modyfikator zastosowany do elementu typu „composable” Text i wyświetlony w podglądzie.

Tworzenie układów niestandardowych

Modyfikator layout zmienia tylko wywołujący element typu „composable”. Aby zmierzyć i ułożyć wiele elementów typu „composable”, użyj zamiast tego elementu typu „composable” Layout. Ten element typu „composable” umożliwia ręczne mierzenie i układanie elementów podrzędnych. Wszystkie układy wyższego poziomu, takie jak Column i Row, są tworzone za pomocą elementu typu „composable” Layout.

Ten przykład tworzy bardzo podstawową wersję Column. Większość układów niestandardowych ma ten sam wzorzec:

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

Podobnie jak w przypadku modyfikatora layout, measurables to lista elementów podrzędnych, które trzeba zmierzyć, a constraints to ograniczenia elementu nadrzędnego. Zgodnie z tą samą logiką MyBasicColumn można zaimplementować w ten sposób:

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

Elementy typu „composable” podrzędne są ograniczone przez ograniczenia Layout (bez ograniczeń minHeight) i są umieszczane na podstawie yPosition poprzedniego elementu typu „composable”.

Oto jak można użyć tego niestandardowego elementu typu „composable”:

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

Kilka elementów tekstowych ułożonych jeden nad drugim w kolumnie.
Rysunek 4. Niestandardowa implementacja Column.

Kierunek układu

Zmień kierunek układu elementu typu „composable”, zmieniając LocalLayoutDirection lokalną kompozycję.

Jeśli umieszczasz elementy typu „composable” ręcznie na ekranie, LayoutDirection jest częścią LayoutScope modyfikatora layout lub elementu typu „composable” Layout.

Gdy używasz layoutDirection, umieszczaj elementy typu „composable” za pomocą place. W przeciwieństwie do metody placeRelative metoda place nie zmienia się w zależności od kierunku układu (od lewej do prawej lub od prawej do lewej).

Układy niestandardowe w praktyce

Więcej informacji o układach i modyfikatorach znajdziesz w artykule Podstawowe układy w Compose, a przykłady układów niestandardowych w praktyce znajdziesz w próbkach Compose, które tworzą układy niestandardowe.

Więcej informacji

Więcej informacji o układach niestandardowych w Compose znajdziesz w tych materiałach.

Filmy