Układy przepływu w sekcji Utwórz

FlowRow i FlowColumn to elementy kompozycyjne podobne do elementów Row i Column, ale różnią się tym, że gdy w kontenerze zabraknie miejsca, przechodzą do następnego wiersza. Spowoduje to utworzenie wielu wierszy lub kolumn. Liczbę elementów w elemencie można też kontrolować za pomocą ustawień maxItemsInEachRow lub maxItemsInEachColumn. Do tworzenia układów elastycznych często możesz używać elementów FlowRow i FlowColumn, ponieważ jeśli elementy są za duże na jeden wymiar, zawartość nie zostanie przycięta. Użycie kombinacji atrybutów 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.

Typowy przykład dotyczy elementu lub interfejsu filtrowania:

5 elementów w wierszu przepływu, pokazując przepełnienie następnego wiersza, gdy nie ma już miejsca.
Rysunek 1. Przykład obiektu FlowRow

Podstawowe użycie

Aby używać FlowRow lub FlowColumn, utwórz taki element i umieść w nim elementy zgodnie ze standardowym procesem:

@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 użytkownika widocznego powyżej, a elementy automatycznie przechodzą do następnego wiersza, gdy w pierwszym wierszu nie będzie już miejsca.

Funkcje układu przepływu

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

Układ osi głównej: układ w poziomie lub w pionie

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

W poniższej tabeli znajdziesz przykłady ustawień horizontalArrangement w elementach w przypadku elementu FlowRow:

Ustawiono układ poziomy w: FlowRow

Wynik

Arrangement.Start (Default)

Elementy rozmieszczone na początku

Arrangement.SpaceBetween

Rozmieszczenie elementów z odstępami między nimi

Arrangement.Center

Przedmioty rozmieszczone na środku

Arrangement.End

Elementy rozmieszczone na końcu

Arrangement.SpaceAround

Przedmioty rozmieszczone wokół własnej osi

Arrangement.spacedBy(8.dp)

Elementy oddzielone określonym dp

W przypadku FlowColumn podobne opcje są dostępne w usłudze verticalArrangement, której domyślna wartość to Arrangement.Top.

Układ osi poprzecznej

Oś krzyżowa to oś w przeciwnym kierunku niż oś główna. Na przykład w polu FlowRow oś pionowa jest widoczna. Aby zmienić układ całej zawartości kontenera na osi krzyżowej, użyj verticalArrangement dla FlowRow i horizontalArrangement dla FlowColumn.

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

Ustawiono ustawienie pionowe w: FlowRow

Wynik

Arrangement.Top (Default)

Układ u góry kontenera

Arrangement.Bottom

Układ u dołu kontenera

Arrangement.Center

Rozmieszczenie centrów kontenerów

W przypadku usługi FlowColumn podobne opcje są dostępne w usłudze horizontalArrangement. Domyślny układ osi krzyżowej to Arrangement.Start.

Wyrównanie poszczególnych elementów

Niektóre elementy możesz umieścić w wierszu z różnym wyrównaniem. Różni się od verticalArrangement i horizontalArrangement, ponieważ wyrównuje elementy w obrębie bieżącego zamówienia. Możesz to zrobić w Modifier.align().

Jeśli np. elementy w polu FlowRow mają różne wysokości, wiersz pobiera wysokość największego elementu i stosuje do nich właściwość Modifier.align(alignmentOption):

W kolumnie FlowRow ustawiono wyrównanie w pionie

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 usługi FlowColumn dostępne są podobne opcje. Domyślnie wyrównanie to Alignment.Start.

Maksymalna liczba elementów w wierszu lub kolumnie

Parametry maxItemsInEachRow i maxItemsInEachColumn określają maksymalną dozwoloną liczbę elementów na osi głównej w jednym wierszu przed zawijaniem do kolejnego. Wartość domyślna to Int.MAX_INT, która zezwala na jak największą liczbę elementów, o ile ich rozmiary pozwalają na zmieszczenie się w wierszu.

Na przykład ustawienie maxItemsInEachRow wymusza w pierwszym układzie tylko 3 elementy:

Nie ustawiono maksymalnej wartości

maxItemsInEachRow = 3

Nie ustawiono maksymalnej wartości w wierszu przepływu Maksymalna liczba elementów ustawiona w wierszu przepływu

Leniwe ładowanie elementów procesu

ContextualFlowRow i ContextualFlowColumn to specjalistyczna wersja FlowRow i FlowColumn, która umożliwia leniwe ładowanie zawartości wiersza lub kolumny przepływu. Dostarczają też informacji o pozycji elementów (indeksie, numerze wiersza i dostępnym rozmiarze), na przykład o tym, czy element znajduje się w pierwszym wierszu. Jest to przydatne w przypadku dużych zbiorów danych oraz wtedy, gdy potrzebujesz informacji kontekstowych o elemencie.

Parametr maxLines ogranicza liczbę wyświetlanych wierszy, a parametr overflow określa, co powinno się wyświetlać po osiągnięciu nadmiaru elementów. Pozwala to określić niestandardową wartość expandIndicator lub collapseIndicator.

Aby na przykład wyświetlić przycisk „+ (liczba pozostałych elementów)” lub „Pokaż mniej”:

val totalCount = 40
var maxLines by remember {
    mutableStateOf(2)
}

val moreOrCollapseIndicator = @Composable { scope: ContextualFlowRowOverflowScope ->
    val remainingItems = totalCount - scope.shownItemCount
    ChipItem(if (remainingItems == 0) "Less" else "+$remainingItems", onClick = {
        if (remainingItems == 0) {
            maxLines = 2
        } else {
            maxLines += 5
        }
    })
}
ContextualFlowRow(
    modifier = Modifier
        .safeDrawingPadding()
        .fillMaxWidth(1f)
        .padding(16.dp)
        .wrapContentHeight(align = Alignment.Top)
        .verticalScroll(rememberScrollState()),
    verticalArrangement = Arrangement.spacedBy(4.dp),
    horizontalArrangement = Arrangement.spacedBy(8.dp),
    maxLines = maxLines,
    overflow = ContextualFlowRowOverflow.expandOrCollapseIndicator(
        minRowsToShowCollapse = 4,
        expandIndicator = moreOrCollapseIndicator,
        collapseIndicator = moreOrCollapseIndicator
    ),
    itemCount = totalCount
) { index ->
    ChipItem("Item $index")
}

Przykładowe wiersze przepływu kontekstowego
Rysunek 2. Przykład obiektu ContextualFlowRow

Wagi produktów

Waga zwiększa wartość elementu na podstawie jego współczynnika i dostępnej przestrzeni w linii, w której został umieszczony. Istotnie jest różnica między FlowRow a Row w kwestii sposobu obliczania szerokości elementu przy użyciu wagi. W przypadku Rows waga jest określana na podstawie wszystkich elementów w elemencie Row. W przypadku FlowRow waga jest określana na podstawie elementów zamówienia, w których znajduje się dany element, a nie wszystkich elementów w kontenerze FlowRow.

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

Aby utworzyć układ przypominający siatkę, możesz użyć kombinacji elementów Modifier.weight i maksymalnej liczby elementów z atrybutem FlowRow lub FlowColumn. Ta metoda przydaje się do tworzenia układów elastycznych, które dostosowują się do rozmiaru urządzenia.

Istnieje kilka różnych przykładów tego, co można osiągnąć za pomocą wag. Jednym z przykładów jest siatka, w której elementy mają równe rozmiary:

Utworzono siatkę z wierszem przepływu
Rysunek 3. Tworzenie siatki za pomocą narzędzia FlowRow

Aby utworzyć siatkę produktów o równych 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)
    }
}

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

Ostatni element w pełnym rozmiarze w siatce
Rysunek 4. Za pomocą polecenia FlowRow możesz utworzyć siatkę, w której ostatni element zajmuje pełną szerokość

Wagi możesz łączyć z innymi właściwościami Modifiers, np. Modifier.width(exactDpAmount), Modifier.aspectRatio(aspectRatio) lub Modifier.fillMaxWidth(fraction). Wszystkie te modyfikatory działają w połączeniu, umożliwiając elastyczne określanie rozmiaru elementów w elemencie FlowRow (lub FlowColumn).

Możesz też utworzyć naprzemienne siatkę o różnych rozmiarach elementów, w której 2 elementy zajmują połowę szerokości, a 1 – na całą szerokość następnej kolumny:

Naprzemienna siatka z wierszem przepływu
Rysunek 5. FlowRow z wierszami o zmiennych rozmiarach

Możesz to zrobić 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))
        }
    }
}

Ułamkowe rozmiary

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

Na przykład ten kod daje różne wyniki w przypadku użycia funkcji FlowRow i 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 z 0,7 ułamkiem szerokości kontenera.

Ułamkowa szerokość z wierszem przepływu

Row: element środkowy zajmujący 0,7 procent pozostałej szerokości (Row).

Ułamkowa szerokość z wierszem

fillMaxColumnWidth()fillMaxRowHeight()

Zastosowanie właściwości Modifier.fillMaxColumnWidth() lub Modifier.fillMaxRowHeight() do elementu w elemencie FlowColumn bądź FlowRow gwarantuje, że elementy w tej samej kolumnie lub wierszu będą mieć taką samą szerokość lub wysokość jak największy element w kolumnie/wierszu.

Na przykład w tym przykładzie używamy słowa FlowColumn, aby wyświetlić listę deserów na Androida. Możesz zobaczyć różnicę w szerokościach poszczególnych elementów, gdy atrybut Modifier.fillMaxColumnWidth() jest stosowany do elementów, a gdy nie i które są 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)
            )
        }
    }
}

Do każdego elementu zastosowano Modifier.fillMaxColumnWidth()

WypełnijMaksymalna Szerokość

Nie ustawiono zmian szerokości (elementy zawijania)

Nie ustawiono maksymalnej szerokości kolumny wypełnienia