Układy niestandardowe

W interfejsie Compose elementy interfejsu są reprezentowane przez funkcje kompozycyjne, które po wywołaniu emitują fragment interfejsu, który jest następnie dodawany do drzewa interfejsu, 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 swoim elemencie nadrzędnym (określonym za pomocą elementów (x, y)) oraz rozmiaru, który określa się za pomocą atrybutów width i height.

Elementy nadrzędne określają ograniczenia dotyczące ich elementów podrzędnych. Należy definiować jego rozmiar w ramach tych ograniczeń. Ograniczenia określają minimalne i maksymalne wartości width i height elementu. Jeśli element ma elementy podrzędne, może mierzyć rozmiar każdego elementu podrzędnego, Gdy element określi i zgłosi własny rozmiar, może określić sposób umieszczania jego elementów podrzędnych względem siebie. Szczegółowe informacje na ten temat znajdziesz w sekcji Tworzenie układów niestandardowych.

Układ każdego węzła w drzewie UI to proces trzyetapowy. Każdy węzeł musi:

  1. Mierz dane dotyczące elementów podrzędnych
  2. Wybór własnego rozmiaru
  3. Umieść elementy podrzędne

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

Użycie zakresów określa, kiedy możesz mierzyć i umieszczać dzieci. Pomiar układu można przeprowadzać tylko podczas przekazywania pomiarów i układu, a element podrzędny można umieszczać tylko podczas przebiegu układu (i dopiero po jego zmierzeniu). Ze względu na zakresy tworzenia, takie jak MeasureScope i PlacementScope, jest to wymuszane podczas kompilacji.

Korzystanie z modyfikatora układu

Korzystając z modyfikatora layout, możesz zmienić sposób pomiaru i układu elementu. Layout to funkcja lambda. Jej parametry obejmują element, który można mierzyć (przekazywany jako measurable), oraz przychodzące ograniczenia funkcji kompozycyjnej przekazywane jako constraints. Niestandardowy modyfikator układu może wyglądać tak:

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

Wyświetlmy Text na ekranie i określ odległość od góry do punktu odniesienia pierwszego wiersza tekstu. Właśnie tak działa modyfikator paddingFromBaseline – wprowadzamy go tutaj jako przykład. Aby to zrobić, użyj modyfikatora layout, aby ręcznie umieścić na ekranie funkcję kompozycyjną. Oto oczekiwane działanie, gdzie Text dopełnienie u góry jest ustawione 24.dp:

Pokazuje różnicę między zwykłym dopełnieniem interfejsu, które ustawia odstęp między elementami, a dopełnieniem tekstu, które ustawia odstęp między jednym elementem bazowym a następną.

Oto kod, który utworzy 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 wartość Text reprezentowaną przez parametr wymierny, wywołując metodę measurable.measure(constraints).
  2. Aby określić rozmiar funkcji kompozycyjnej, wywołaj metodę layout(width, height), która daje również funkcję lambda używaną do umieszczania elementów opakowanych. W tym przypadku jest to wysokość między ostatnią podstawą a dodanym górnym dopełnieniem.
  3. Aby umieścić opakowane elementy na ekranie, wywołaj placeable.place(x, y). Jeśli opakowane elementy nie zostaną umieszczone, nie będą widoczne. Pozycja y odpowiada górnego dopełnieniu, czyli pozycji pierwszej linii bazowej tekstu.

Aby sprawdzić, czy wszystko działa zgodnie z oczekiwaniami, użyj tego modyfikatora na 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łe dopełnienie między elementami, a drugi pokazuje dopełnienie od jednej linii bazowej do kolejnej

Tworzenie układów niestandardowych

Modyfikator layout zmienia tylko funkcję wywoływania funkcji kompozycyjnej. Aby mierzyć i układać wiele elementów kompozycyjnych, użyj funkcji Layout kompozycyjnej. Ten element kompozycyjny umożliwia ręczne mierzenie i rozmieszczanie elementów dzieci. Wszystkie układy wyższego poziomu, takie jak Column i Row, są tworzone za pomocą funkcji kompozycyjnej Layout.

Utwórzmy podstawową wersję Column. Większość układów niestandardowych stosuje się do tego wzorca:

@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 mają być objęte pomiarem, a constraints to ograniczenia elementu nadrzędnego. Zgodnie z tą samą logiką co wcześniej, element MyBasicColumn można wdrożyć 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
            }
        }
    }
}

Podrzędne elementy kompozycyjne są objęte ograniczeniami Layout (bez ograniczeń minHeight) i są rozmieszczane na podstawie funkcji yPosition poprzedniego elementu kompozycyjnego.

Oto jak zostanie użyty 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 układanych jeden nad drugim w kolumnie.

Kierunek układu

Aby zmienić kierunek układu elementu kompozycyjnego, zmień lokalną kompozycję LocalLayoutDirection.

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

Jeśli używasz layoutDirection, umieszczaj kompozycje za pomocą place. W przeciwieństwie do metody placeRelative parametr place nie zmienia się zgodnie z kierunkiem układu (od lewej do prawej lub od prawej do lewej).

Układy niestandardowe w działaniu

Więcej informacji o układach i modyfikatorach znajdziesz w sekcji o podstawowych układach w obszarze tworzenia wiadomości, a w sekcji o przykładach tworzenia układów niestandardowych dowiesz się, jak działają układy niestandardowe.

Więcej informacji

Więcej informacji o układach niestandardowych w sekcji Utwórz znajdziesz w tych dodatkowych materiałach.

Filmy