Układy przepływu w Compose

FlowRow i FlowColumn to komponenty podobne do Row i Column, ale różniące się tym, że elementy przechodzą do następnego wiersza, gdy w kontenerze zabraknie miejsca. Powoduje to utworzenie wielu wierszy lub kolumn. Liczbę elementów w wierszu można też kontrolować, ustawiając maxItemsInEachRow lub maxItemsInEachColumn. Często możesz używać FlowRow i FlowColumn do tworzenia układów responsywnych – treść nie zostanie obcięta, jeśli elementy są zbyt duże w jednym wymiarze. Użycie kombinacji maxItemsInEach* z 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 to interfejs układu chipów lub filtrowania:

5 elementów w FlowRow, które w przypadku braku miejsca przechodzą do następnego wiersza.
Rysunek 1. Przykład FlowRow

Podstawowe użycie

Aby użyć FlowRow lub FlowColumn, utwórz te komponenty i umieść w nich elementy, które mają być wyświetlane w standardowym układzie:

@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 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ływowego

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

Rozmieszczenie wzdłuż osi głównej: rozmieszczenie poziome lub pionowe

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

W tabeli poniżej znajdziesz przykłady ustawiania horizontalArrangement w elementach FlowRow:

Rozmieszczenie poziome ustawione w FlowRow

Wynik

Arrangement.Start (Default)

Elementy uporządkowane według początku

Arrangement.SpaceBetween

Rozmieszczenie elementów z odstępami

Arrangement.Center

Elementy ułożone na środku

Arrangement.End

Elementy ułożone na końcu

Arrangement.SpaceAround

Elementy rozmieszczone z zachowaniem odstępów

Arrangement.spacedBy(8.dp)

Elementy oddalone od siebie o określoną liczbę pikseli

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

Rozmieszczenie wzdłuż 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 w kontenerze wzdłuż osi poprzecznej, użyj verticalArrangement w FlowRow i horizontalArrangement w FlowColumn.

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

Rozmieszczenie pionowe ustawione w FlowRow

Wynik

Arrangement.Top (Default)

Układ górnej części kontenera

Arrangement.Bottom

Układ dolnej części kontenera

Arrangement.Center

Ułożenie elementów w kontenerze

W przypadku FlowColumn podobne opcje są dostępne w horizontalArrangement. Domyślne rozmieszczenie wzdłuż 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ę to od verticalArrangement i horizontalArrangement, ponieważ wyrównuje elementy w bieżącym wierszu. Możesz to zrobić za pomocą Modifier.align().

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

Wyrównanie pionowe ustawione w 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 wzdłuż osi głównej, które mogą znajdować się w jednym wierszu przed zawinięciem do następnego. Wartość domyślna to Int.MAX_INT, która pozwala na umieszczenie jak największej liczby elementów, o ile ich rozmiary pozwalają na zmieszczenie się w wierszu.

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

Nie ustawiono maksymalnej liczby elementów

maxItemsInEachRow = 3

Brak maksymalnej wartości w wierszu pliku danych Maksymalna liczba elementów ustawiona w wierszu przepływu

Wagi elementów

Waga zwiększa rozmiar elementu na podstawie jego współczynnika i dostępnego miejsca w wierszu, w którym został umieszczony. Ważne jest, że istnieje różnica między FlowRow a Row w sposobie używania wag do obliczania szerokości elementu. W przypadku Rows waga jest oparta na wszystkich elementach w Row. W przypadku FlowRow waga jest oparta na elementach w wierszu, w którym umieszczony jest element, a nie na wszystkich elementach w kontenerze FlowRow.

Jeśli na przykład masz 4 elementy, które mieszczą się w jednym wierszu, a ich wagi to odpowiednio 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ą wzoru: 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 responsywnych, które dostosowują się do rozmiaru urządzenia.

Oto kilka 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, jak pokazano poniżej:

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

Aby utworzyć siatkę elementó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)
    }
}

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. Tworzenie siatki za pomocą FlowRow, 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). Te modyfikatory działają razem, aby umożliwić responsywne określanie rozmiarów elementów w FlowRow (lub FlowColumn).

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

Naprzemienna siatka z wierszem przepływu
Rysunek 4. FlowRow z naprzemiennymi rozmiarami wierszy

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

Określanie rozmiarów ułamkowych

Za pomocą Modifier.fillMaxWidth(fraction) możesz określić rozmiar kontenera, który ma zajmować element. Różni się to od sposobu działania Modifier.fillMaxWidth(fraction) w przypadku zastosowania do Row lub Column, w tym Row/Column elementy zajmują procent pozostałej szerokości, a nie całej szerokości 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: środkowy element zajmuje 0,7 ułamka całej szerokości kontenera.

Ułamkowa szerokość z wierszem przepływu

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

Ułamkowa szerokość wiersza

fillMaxColumnWidth() i fillMaxRowHeight()

Zastosowanie Modifier.fillMaxColumnWidth() lub Modifier.fillMaxRowHeight() do elementu w FlowColumn lub FlowRow powoduje, że elementy w tej samej kolumnie lub wierszu zajmują taką samą szerokość lub wysokość jak największy element w kolumnie lub wierszu.

Na przykład ten przykład używa FlowColumn do wyświetlania listy deserów na Androidzie. Możesz zobaczyć różnicę w szerokości poszczególnych elementów, gdy do elementów zastosowano Modifier.fillMaxColumnWidth(), a gdy nie zastosowano i elementy 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)
            )
        }
    }
}

Modifier.fillMaxColumnWidth() zastosowany do każdego elementu

fillMaxColumnWidth

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

Nie ustawiono maksymalnej szerokości kolumny wypełnienia