Układy przepływu w Compose

FlowRow i FlowColumn to elementy kompozycyjne podobne do Row i Column, ale różnią się tym, że elementy przechodzą do następnego wiersza, gdy w kontenerze zabraknie miejsca. Spowoduje to utworzenie wielu wierszy lub kolumn. Liczbę elementów w wierszu można też kontrolować, ustawiając maxItemsInEachRow lub maxItemsInEachColumn. Do tworzenia układów elastycznych często można używać właściwości FlowRow i FlowColumn – treść nie zostanie odcięta, jeśli elementy będą zbyt duże dla jednego wymiaru, a użycie kombinacji właściwości maxItemsInEach* i Modifier.weight(weight) może pomóc w tworzeniu układów, które w razie potrzeby wypełniają lub rozszerzają szerokość wiersza lub kolumny.

Typowym przykładem jest element UI do filtrowania:

5 elementów w FlowRow, które przechodzą do następnego wiersza, gdy nie ma już miejsca.
Rysunek 1. Przykład: FlowRow

Podstawowe użycie

Aby użyć FlowRow lub FlowColumn, utwórz te funkcje kompozycyjne i umieść w nich elementy, które powinny być zgodne ze standardowym przepływem:

@Composable
private fun FlowRowSimpleUsageExample() {
    FlowRow(modifier = Modifier.padding(8.dp)) {
        ChipItem("Price: High to Low")
        ChipItem("Avg rating: 4+")
        ChipItem("Free breakfast")
        ChipItem("Free cancellation")
        ChipItem("£50 pn")
    }
}

Ten fragment kodu powoduje wyświetlenie interfejsu pokazanego powyżej, w którym elementy automatycznie przechodzą do następnego wiersza, gdy w pierwszym wierszu nie ma już miejsca.

Funkcje układu przepływu

Układy przepływu mają te funkcje i właściwości, których możesz używać do tworzenia różnych układów w aplikacji.

Układ osi głównej: układ poziomy lub pionowy.

Oś główna to oś, na której są rozmieszczone elementy (np. w FlowRow elementy są rozmieszczone poziomo). Parametr horizontalArrangement w FlowRow określa sposób rozdzielania wolnego miejsca między elementami.

W tabeli poniżej znajdziesz przykłady ustawienia horizontalArrangement dla elementów w przypadku FlowRow:

Układ poziomy ustawiono na: FlowRow

Wynik

Arrangement.Start (Default)

Elementy ułożone od początku

Arrangement.SpaceBetween

Układ elementów z odstępami między nimi

Arrangement.Center

Elementy ułożone na środku

Arrangement.End

Elementy ułożone na końcu

Arrangement.SpaceAround

Elementy ułożone z odstępami wokół nich

Arrangement.spacedBy(8.dp)

Elementy oddzielone określoną liczbą dp

W przypadku FlowColumn podobne opcje są dostępne w verticalArrangement, a domyślna wartość to Arrangement.Top.

Układ osi poprzecznej

Oś poprzeczna to oś w kierunku przeciwnym do osi głównej. Na przykład w FlowRow jest to oś pionowa. Aby zmienić sposób rozmieszczenia całej zawartości kontenera na osi poprzecznej, użyj verticalArrangement dla FlowRow i horizontalArrangement dla FlowColumn.

W przypadku FlowRow w tabeli poniżej znajdziesz przykłady ustawiania różnych wartości verticalArrangement w elementach:

Układ pionowy ustawiony na FlowRow

Wynik

Arrangement.Top (Default)

Układ kontenera u góry

Arrangement.Bottom

Aranżacja kontenera od dołu

Arrangement.Center

Układ środkowy kontenera

W przypadku FlowColumn podobne opcje są dostępne w horizontalArrangement. Domyślny układ osi poprzecznej to Arrangement.Start.

Wyrównanie poszczególnych elementów

Możesz chcieć umieścić poszczególne elementy w wierszu z różnymi wyrównaniami. Różni się od verticalArrangement i horizontalArrangement, ponieważ wyrównuje elementy w bieżącym wierszu. Możesz to zastosować za pomocą Modifier.align().

Na przykład gdy elementy w komponencie FlowRow mają różne wysokości, wiersz przyjmuje wysokość największego elementu i stosuje do elementów właściwość Modifier.align(alignmentOption):

Wyrównanie w pionie ustawiono na: FlowRow

Wynik

Alignment.Top (Default)

Elementy wyrównane do góry

Alignment.Bottom

Elementy wyrównane do dołu

Alignment.CenterVertically

Elementy wyrównane do środka

W przypadku FlowColumn dostępne są podobne opcje. Domyślne wyrównanie to Alignment.Start.

Maksymalna liczba elementów w wierszu lub kolumnie

Parametry maxItemsInEachRow lub maxItemsInEachColumn określają maksymalną liczbę elementów na osi głównej, które mogą znajdować się w jednym wierszu, zanim zostaną przeniesione do następnego. Wartość domyślna to Int.MAX_INT, co pozwala na wyświetlanie jak największej liczby elementów, o ile ich rozmiary pozwalają na zmieszczenie ich w wierszu.

Na przykład ustawienie maxItemsInEachRow wymusza, aby początkowy układ zawierał tylko 3 elementy:

Nie ustawiono wartości maksymalnej

maxItemsInEachRow = 3

Nie ustawiono maksymalnej liczby wierszy przepływu Maksymalna liczba elementów ustawiona w wierszu przepływu

Wagi elementów

Waga powiększa element na podstawie jego współczynnika i dostępnego miejsca w wierszu, w którym został umieszczony. Ważne jest to, że FlowRow i Row różnią się sposobem używania wag do obliczania szerokości elementu. W przypadku Rows waga jest określana na podstawie wszystkich elementów w Row. W przypadku FlowRow waga jest określana na podstawie elementów w wierszu, w którym znajduje się dany element, a nie wszystkich elementów w kontenerze FlowRow.

Jeśli na przykład masz 4 elementy, które znajdują się na linii, każdy z inną wagą: 1f, 2f, 1f i 3f, łączna waga wynosi 7f. Pozostałe miejsce w wierszu lub kolumnie zostanie podzielone przez 7f. Następnie szerokość każdego elementu zostanie obliczona za pomocą: weight * (remainingSpace / totalWeight).

Możesz użyć kombinacji Modifier.weight i maksymalnej liczby elementów z FlowRow lub FlowColumn, aby utworzyć układ przypominający siatkę. To podejście jest przydatne do tworzenia układów elastycznych, które dostosowują się do rozmiaru urządzenia.

Oto kilka przykładów tego, co możesz osiągnąć, używając wag. Przykładem jest siatka, w której elementy mają taki sam rozmiar, jak pokazano poniżej:

Siatka utworzona z wierszem przepływu
Rysunek 2. Tworzenie siatki za pomocą FlowRow

Aby utworzyć siatkę z elementami o jednakowych rozmiarach, możesz wykonać te czynności:

val rows = 3
val columns = 3
FlowRow(
    modifier = Modifier.padding(4.dp),
    horizontalArrangement = Arrangement.spacedBy(4.dp),
    maxItemsInEachRow = rows
) {
    val itemModifier = Modifier
        .padding(4.dp)
        .height(80.dp)
        .weight(1f)
        .clip(RoundedCornerShape(8.dp))
        .background(MaterialColors.Blue200)
    repeat(rows * columns) {
        Spacer(modifier = itemModifier)
    }
}

Co ważne, jeśli dodasz kolejny element i powtórzysz go 10 razy zamiast 9, ostatni element zajmie całą ostatnią kolumnę, ponieważ łączna waga całego wiersza wynosi 1f:

Ostatni element w pełnym rozmiarze w siatce
Rysunek 3. Używanie FlowRow do tworzenia siatki, w której ostatni element zajmuje całą szerokość

Możesz łączyć wagi z innymi Modifiers, takimi jak Modifier.width(exactDpAmount), Modifier.aspectRatio(aspectRatio) lub Modifier.fillMaxWidth(fraction). Wszystkie te modyfikatory działają razem, umożliwiając elastyczne określanie rozmiaru elementów w komponencie FlowRow (lub FlowColumn).

Możesz też utworzyć siatkę z naprzemiennymi elementami o różnych rozmiarach, w której 2 elementy zajmują po połowie szerokości, a 1 element zajmuje całą szerokość następnej kolumny:

Siatka naprzemienna z wierszem przepływu
Rysunek 4. FlowRow z wierszami o różnych rozmiarach

Można to osiągnąć za pomocą tego kodu:

FlowRow(
    modifier = Modifier.padding(4.dp),
    horizontalArrangement = Arrangement.spacedBy(4.dp),
    maxItemsInEachRow = 2
) {
    val itemModifier = Modifier
        .padding(4.dp)
        .height(80.dp)
        .clip(RoundedCornerShape(8.dp))
        .background(Color.Blue)
    repeat(6) { item ->
        // if the item is the third item, don't use weight modifier, but rather fillMaxWidth
        if ((item + 1) % 3 == 0) {
            Spacer(modifier = itemModifier.fillMaxWidth())
        } else {
            Spacer(modifier = itemModifier.weight(0.5f))
        }
    }
}

Rozmiary ułamkowe

Za pomocą Modifier.fillMaxWidth(fraction) możesz określić rozmiar kontenera, który powinien zajmować element. Różni się to od działania właściwości Modifier.fillMaxWidth(fraction) w przypadku Row lub Column, ponieważ elementy Row/Column zajmują procent pozostałej szerokości, a nie szerokości całego kontenera.

Na przykład ten kod daje różne wyniki w zależności od tego, czy używasz FlowRow, czy Row:

FlowRow(
    modifier = Modifier.padding(4.dp),
    horizontalArrangement = Arrangement.spacedBy(4.dp),
    maxItemsInEachRow = 3
) {
    val itemModifier = Modifier
        .clip(RoundedCornerShape(8.dp))
    Box(
        modifier = itemModifier
            .height(200.dp)
            .width(60.dp)
            .background(Color.Red)
    )
    Box(
        modifier = itemModifier
            .height(200.dp)
            .fillMaxWidth(0.7f)
            .background(Color.Blue)
    )
    Box(
        modifier = itemModifier
            .height(200.dp)
            .weight(1f)
            .background(Color.Magenta)
    )
}

FlowRow: element środkowy o szerokości równej 0,7 szerokości całego kontenera.

Szerokość ułamkowa z wierszem przepływu

Row: element środkowy zajmuje 0,7% pozostałej szerokości (Row).

Szerokość ułamkowa z wierszem

fillMaxColumnWidth() i fillMaxRowHeight()

Zastosowanie właściwości Modifier.fillMaxColumnWidth() lub Modifier.fillMaxRowHeight() do elementu w kontenerze FlowColumn lub FlowRow sprawia, że elementy w tej samej kolumnie lub wierszu mają taką samą szerokość lub wysokość jak największy element w kolumnie lub wierszu.

W tym przykładzie używamy FlowColumn do wyświetlania listy deserów Androida. Możesz zobaczyć różnicę w szerokości każdego elementu, gdy zastosujesz Modifier.fillMaxColumnWidth() i gdy tego nie zrobisz, a elementy będą zawijane.

FlowColumn(
    Modifier
        .padding(20.dp)
        .fillMaxHeight()
        .fillMaxWidth(),
    horizontalArrangement = Arrangement.spacedBy(8.dp),
    verticalArrangement = Arrangement.spacedBy(8.dp),
    maxItemsInEachColumn = 5,
) {
    repeat(listDesserts.size) {
        Box(
            Modifier
                .fillMaxColumnWidth()
                .border(1.dp, Color.DarkGray, RoundedCornerShape(8.dp))
                .padding(8.dp)
        ) {

            Text(
                text = listDesserts[it],
                fontSize = 18.sp,
                modifier = Modifier.padding(3.dp)
            )
        }
    }
}

Zastosowano Modifier.fillMaxColumnWidth() do każdego elementu

fillMaxColumnWidth

Nie ustawiono zmian szerokości (zawijanie elementów)

Nie ustawiono maksymalnej szerokości kolumny bez wypełnienia