Układy niestandardowe

W Compose elementy interfejsu są reprezentowane przez funkcje kompozycyjne, które generują części interfejsu użytkownika przy wywołaniu. Jest on dodawany do drzewa UI, który jest renderowany 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), a funkcja rozmiar określony jako width i height.

Elementy nadrzędne definiują ograniczenia dla elementów podrzędnych. Żądanie elementu i określić jego rozmiar w ramach tych ograniczeń. Ograniczenia maksymalnie width i height elementu. Jeśli element ma elementy podrzędne, może zmierzyć każde dziecko, aby określić jego wielkość. Gdy element określi, i raportuje własny rozmiar, może określić, w jaki sposób elementów względem siebie, jak opisano szczegółowo w sekcji Tworzenie układy scalone.

Ułożenie każdego węzła w drzewie interfejsu obejmuje proces trzyetapowy. Każdy węzeł musi:

  1. Mierz wszystkie dzieci
  2. Określanie własnego rozmiaru
  3. Umieść elementy podrzędne

Trzy etapy układu węzłów: pomiar elementów podrzędnych, wybór rozmiaru, umieszczanie elementów podrzędnych

Użycie zakresów określa, kiedy możesz mierzyć i umieszczać dane dzieci. Pomiar układu można przeprowadzić tylko podczas pomiarów i kart układu, , a element podrzędny można umieścić tylko podczas kart układu (i dopiero po mierzonych wartości). Ze względu na zakresy tworzenia wiadomości, takie jak MeasureScope i PlacementScope, jest wymuszane podczas kompilacji.

Używanie modyfikatora układu

Aby zmienić sposób pomiaru i układu elementu, możesz użyć modyfikatora layout na zewnątrz. Layout to funkcja lambda; w jego parametrach obejmują m.in. element, który można zmierzyć przekazywane jako measurable, a ograniczenia przychodzące funkcji kompozycyjnej przekazywane jako constraints Niestandardowy modyfikator układu może wyglądać tak:

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

Wyświetlmy na ekranie znak Text i ustawmy odległość od góry do jest to punkt odniesienia dla pierwszego wiersza tekstu. Właśnie to jest paddingFromBaseline pokazujemy go na przykładzie. Aby to zrobić, użyj modyfikatora layout i ręcznie umieść funkcję kompozycyjną w elemencie ekranu. Oto oczekiwane działanie, w przypadku którego dopełnienie u góry Text jest ustawione na 24.dp:

Pokazuje różnicę między normalnym dopełnieniem interfejsu, które ustawia odstęp między elementami, a dopełnieniem tekstu ustawiającym odstęp od punktu odniesienia do następnej.

Oto kod do utworzenia odstępów:

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 zawiera ten kod:

  1. W parametrze lambda funkcji measurable mierzysz wartość Text reprezentowaną przez wymierny parametr, wywołując funkcję measurable.measure(constraints).
  2. Aby określić rozmiar funkcji kompozycyjnej, wywołaj funkcję layout(width, height) , która daje też parametr lambda używany do umieszczania opakowanych elementów. W w tym przypadku jest to wysokość między ostatnią bazą bazową a dodanym dopełnieniem u góry.
  3. Pozycjonujesz opakowane elementy na ekranie, wywołując placeable.place(x, y) Jeśli opakowane elementy nie zostaną umieszczone, nie zostaną umieszczone widoczne. Położenie y odpowiada górnemu dopełnieniu, czyli położeniu i pierwszy punkt odniesienia.

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

wielu podglądów elementów tekstowych; jeden pokazuje zwykłe dopełnienie między elementami, a drugie – dopełnienie od jednego punktu odniesienia do kolejnego.

Tworzenie układów niestandardowych

Modyfikator layout zmienia tylko funkcję wywoływania kompozycyjnego. Do pomiaru i układu wiele elementów kompozycyjnych, użyj funkcji Layout. Ta funkcja kompozycyjna umożliwia ręczne pomiary i rozmieszczanie dzieci. Wszystkie układy wyższego poziomu takie jak Column i Row są tworzone za pomocą funkcji Layout.

Stwórzmy podstawową wersję interfejsu Column. W większości układów niestandardowych wzór:

@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 modyfikator layout, measurables to lista elementów podrzędnych, które które należy objąć pomiarem, a constraints to ograniczenia z elementu nadrzędnego. Korzystając z tej samej logiki co wcześniej, można zaimplementować MyBasicColumn w taki sposób: to:

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

Podrzędne elementy kompozycyjne są ograniczone przez ograniczenia Layout (bez minHeight) i są one umieszczane na podstawie yPosition funkcji poprzedniego elementu kompozycyjnego.

Oto jak można wykorzystać ten niestandardowy element kompozycyjny:

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

Kierunek układu

Aby zmienić kierunek układu funkcji kompozycyjnej, zmień LocalLayoutDirection kompozycje lokalne.

Jeśli ręcznie umieszczasz na ekranie kompozycje, element LayoutDirection będzie część LayoutScope modyfikatora layout lub elementu kompozycyjnego Layout.

Jeśli używasz właściwości layoutDirection, do tworzenia funkcji kompozycyjnych używaj funkcji place. W przeciwieństwie do placeRelative metody 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 Podstawowe układy w funkcji tworzenia wiadomości, i zobacz, jak działają układy niestandardowe Utwórz przykładowe układy niestandardowe.

Więcej informacji

Więcej informacji o układach niestandardowych w funkcji tworzenia wiadomości znajdziesz w tych dodatkowych artykułach i zasobami Google Cloud.

Filmy

. .