Układy niestandardowe

W Compose elementy interfejsu są reprezentowane przez funkcje typu „composable”, które po wywołaniu emitują fragment interfejsu, który jest następnie dodawany do drzewa interfejsu renderowanego 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.

Rodzice definiują ograniczenia dla elementów podrzędnych. Element musi określić swój rozmiar w ramach tych ograniczeń. Ograniczenia określają minimalną i maksymalną wartość 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ć sposób umieszczania elementów podrzędnych względem siebie, jak opisano szczegółowo w artykule Tworzenie układów niestandardowych.

Układanie każdego węzła w drzewie interfejsu to proces składający się z 3 kroków. Każdy węzeł musi:

  1. Mierzenie elementów podrzędnych
  2. samodzielnie określać swój rozmiar,
  3. Umieść elementy podrzędne
Trzy etapy układu węzła: mierzenie elementów podrzędnych, określanie rozmiaru, umieszczanie elementów podrzędnych
Rysunek 1. Trzy etapy układu węzła to: mierzenie elementów podrzędnych, określanie rozmiaru i umieszczanie elementów podrzędnych.

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

Używanie modyfikatora układu

Możesz użyć modyfikatora layout, aby zmienić sposób pomiaru i układu elementu. Layout to lambda, której parametry obejmują element, który można zmierzyć, przekazywany jako measurable, oraz ograniczenia przychodzące tego elementu kompozycyjnego, przekazywane jako constraints. Modyfikator układu niestandardowego 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. Tutaj implementujesz go jako przykład. Aby to zrobić, użyj modyfikatora layout, aby ręcznie umieścić element kompozycyjny na ekranie. Oto wynik, gdy dopełnienie górne Text jest ustawione na 24.dp:

Ilustracja pokazująca różnicę między normalnym dopełnieniem interfejsu, które określa odstępy między elementami, a dopełnieniem tekstu, które określa odstępy między liniami bazowymi
Rysunek 2. Zastosowano tekst z: paddingFromBaseline.

Oto kod, który pozwala uzyskać takie 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)
    }
}

Oto co się dzieje w tym kodzie:

  1. W parametrze lambda measurable mierzysz Text reprezentowany przez mierzalny parametr, wywołując measurable.measure(constraints).
  2. Rozmiar elementu kompozycyjnego określasz, wywołując metodę layout(width, height), która udostępnia też funkcję lambda używaną do umieszczania elementów opakowanych. W tym przypadku jest to wysokość między ostatnią linią bazową a dodanym górnym dopełnieniem.
  3. Elementy opakowane umieszczasz na ekranie, wywołując funkcję placeable.place(x, y). Jeśli elementy zawijane nie zostaną umieszczone, nie będą widoczne. Pozycja y odpowiada górnemu dopełnieniu: 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))
    }
}

Kilka podglądów elementów tekstowych; jeden pokazuje zwykłe dopełnienie między elementami, a drugi dopełnienie od jednej linii bazowej do następnej.
Rysunek 3. Modyfikator zastosowany do funkcji kompozycyjnej Text i wyświetlony w podglądzie.

Tworzenie układów niestandardowych

Modyfikator layout zmienia tylko wywołujący element kompozycyjny. Aby zmierzyć i rozmieścić wiele funkcji kompozycyjnych, użyj funkcji kompozycyjnej Layout. Ten element kompozycyjny 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ą funkcji kompozycyjnej Layout.

.

W tym przykładzie tworzymy bardzo podstawową wersję Column. Większość układów niestandardowych ma taki 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 należy zmierzyć, a constraints to ograniczenia z elementu nadrzędnego. Zgodnie z tą samą logiką co wcześniej 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 kompozycyjne podrzędne są ograniczone przez ograniczenia Layout (bez ograniczeń minHeight) i są umieszczane na podstawie yPosition poprzedniego elementu kompozycyjnego.

Oto jak można użyć tego niestandardowego elementu kompozycyjnego:

@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 usługi Column.

Kierunek układu

Zmień kierunek układu elementu kompozycyjnego, zmieniając kompozycję lokalną LocalLayoutDirection.

Jeśli ręcznie umieszczasz elementy kompozycyjne na ekranie, LayoutDirection jest częścią LayoutScope modyfikatora layout lub elementu kompozycyjnego Layout.

Podczas korzystania z layoutDirection umieszczaj komponenty za pomocą place. W przeciwieństwie do metody placeRelative właściwość 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. Przykłady niestandardowych układów znajdziesz w przykładowych kodach Compose, które tworzą niestandardowe układy.

Więcej informacji

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

Filmy