Ablauflayouts in Compose

FlowRow und FlowColumn sind Elemente, die Row und Column ähneln, sich aber dadurch unterscheiden, dass Elemente in die nächste Zeile fließen, wenn im Container kein Platz mehr vorhanden ist. Dadurch werden mehrere Zeilen oder Spalten erstellt. Die Anzahl der Elemente in einer Zeile kann auch durch Festlegen von maxItemsInEachRow oder maxItemsInEachColumn gesteuert werden. Mit FlowRow und FlowColumn lassen sich oft responsive Layouts erstellen. Inhalte werden nicht abgeschnitten, wenn Elemente für eine Dimension zu groß sind. Mit einer Kombination aus maxItemsInEach* und Modifier.weight(weight) können Sie Layouts erstellen, die bei Bedarf die Breite einer Zeile oder Spalte füllen oder maximieren.

Ein typisches Beispiel ist eine Chip- oder Filteroberfläche:

5 Chips in einer FlowRow, die den Überlauf in die nächste Zeile anzeigen, wenn kein Platz mehr verfügbar ist.
Abbildung 1: Beispiel für FlowRow

Grundlegende Verwendung

Wenn du FlowRow oder FlowColumn verwenden möchtest, erstelle diese Composeables und platziere die Elemente darin, die dem Standardablauf folgen sollen:

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

Dieses Snippet führt zur oben gezeigten Benutzeroberfläche, bei der Elemente automatisch in die nächste Zeile verschoben werden, wenn in der ersten Zeile kein Platz mehr vorhanden ist.

Merkmale des Flusslayouts

Flow-Layouts haben die folgenden Funktionen und Eigenschaften, mit denen Sie verschiedene Layouts in Ihrer App erstellen können.

Anordnung der Hauptachse: horizontal oder vertikal

Die Hauptachse ist die Achse, entlang der die Elemente angeordnet sind. In FlowRow sind die Elemente beispielsweise horizontal angeordnet. Mit dem Parameter horizontalArrangement in FlowRow wird festgelegt, wie der kostenlose Speicherplatz zwischen den Elementen verteilt wird.

In der folgenden Tabelle finden Sie Beispiele für die Festlegung von horizontalArrangement für Elemente für FlowRow:

Horizontale Anordnung auf FlowRow festgelegt

Ergebnis

Arrangement.Start (Default)

Elemente nach Beginn angeordnet

Arrangement.SpaceBetween

Anordnung der Elemente mit Zwischenraum

Arrangement.Center

Elemente in der Mitte angeordnet

Arrangement.End

Elemente am Ende angeordnet

Arrangement.SpaceAround

Elemente, die mit einem Abstand angeordnet sind

Arrangement.spacedBy(8.dp)

Elemente mit einem bestimmten Abstand in Pixeln

Für FlowColumn sind ähnliche Optionen mit verticalArrangement verfügbar. Die Standardeinstellung ist Arrangement.Top.

Querachsenanordnung

Die Querachse ist die Achse in der entgegengesetzten Richtung zur Hauptachse. In FlowRow ist das beispielsweise die vertikale Achse. Wenn Sie ändern möchten, wie der gesamte Inhalt im Container in der Querachse angeordnet wird, verwenden Sie verticalArrangement für FlowRow und horizontalArrangement für FlowColumn.

In der folgenden Tabelle sind Beispiele für die Festlegung verschiedener verticalArrangement für die Elemente zu sehen:FlowRow

Vertikale Anordnung auf FlowRow festgelegt

Ergebnis

Arrangement.Top (Default)

Containeroberteil

Arrangement.Bottom

Containerboden

Arrangement.Center

Container-Mitte

Für FlowColumn sind ähnliche Optionen mit horizontalArrangement verfügbar. Die Standardanordnung der Querachse ist Arrangement.Start.

Ausrichtung einzelner Artikel

Sie können einzelne Elemente innerhalb der Zeile mit unterschiedlichen Ausrichtungen positionieren. Das unterscheidet sich von verticalArrangement und horizontalArrangement, da die Elemente innerhalb der aktuellen Zeile ausgerichtet werden. Das können Sie mit Modifier.align() tun.

Wenn Elemente in einer FlowRow beispielsweise unterschiedliche Höhen haben, wird für die Zeile die Höhe des größten Elements übernommen und Modifier.align(alignmentOption) auf die Elemente angewendet:

Vertikale Ausrichtung auf FlowRow festgelegt

Ergebnis

Alignment.Top (Default)

Elemente, die oben ausgerichtet sind

Alignment.Bottom

Elemente, die unten ausgerichtet sind

Alignment.CenterVertically

Elemente zentriert ausrichten

Für FlowColumn sind ähnliche Optionen verfügbar. Die Standardausrichtung ist Alignment.Start.

Maximale Anzahl von Elementen in Zeile oder Spalte

Mit den Parametern maxItemsInEachRow oder maxItemsInEachColumn wird die maximale Anzahl der Elemente in der Hauptachse definiert, die in einer Zeile zulässig sind, bevor der Text auf die nächste Zeile umgebrochen wird. Die Standardeinstellung ist Int.MAX_INT. Damit sind so viele Elemente wie möglich zulässig, sofern sie in die Zeile passen.

Wenn Sie beispielsweise ein maxItemsInEachRow festlegen, hat das ursprüngliche Layout nur drei Elemente:

Kein Maximum festgelegt

maxItemsInEachRow = 3

In der Ablaufzeile ist kein Maximum festgelegt. Maximale Anzahl von Elementen in der Ablaufzeile festgelegt

Lazy Loading-Elemente für den Navigationsfluss

ContextualFlowRow und ContextualFlowColumn sind eine spezielle Version von FlowRow und FlowColumn, mit der Sie den Inhalt Ihrer Flusszeile oder ‑spalte per Lazy Load laden können. Außerdem enthalten sie Informationen zur Position des Artikels (Index, Zeilennummer und verfügbare Größe), z. B. ob sich der Artikel in der ersten Zeile befindet. Das ist nützlich bei großen Datenmengen und wenn Sie Kontextinformationen zu einem Artikel benötigen.

Mit dem Parameter maxLines wird die Anzahl der angezeigten Zeilen begrenzt. Der Parameter overflow gibt an, was angezeigt werden soll, wenn die maximale Anzahl von Elementen erreicht wird. Sie können eine benutzerdefinierte expandIndicator oder collapseIndicator angeben.

So können Sie beispielsweise die Schaltfläche „+ (Anzahl der verbleibenden Artikel)“ oder „Weniger anzeigen“ anzeigen lassen:

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")
}

Beispiel für Zeilen mit kontextbezogenen Abläufen
Abbildung 2: Beispiel für ContextualFlowRow

Artikelgewichte

Mit dem Gewicht wird ein Element basierend auf seinem Faktor und dem verfügbaren Platz in der Zeile, in der es platziert wurde, vergrößert. Ein wichtiger Unterschied zwischen FlowRow und Row besteht darin, wie Gewichte zur Berechnung der Breite eines Elements verwendet werden. Bei Rows basiert das Gewicht auf allen Elementen in der Row. Bei FlowRow basiert das Gewicht auf den Elementen in der Zeile, in der sich ein Element befindet, nicht auf allen Elementen im FlowRow-Container.

Wenn Sie beispielsweise vier Artikel haben, die alle auf einer Linie liegen, und die jeweils unterschiedliche Gewichte von 1f, 2f, 1f und 3f haben, beträgt das Gesamtgewicht 7f. Der verbleibende Platz in einer Zeile oder Spalte wird durch 7f geteilt. Die Breite der einzelnen Artikel wird dann mit folgender Formel berechnet: weight * (remainingSpace / totalWeight).

Sie können eine Kombination aus Modifier.weight- und Max-Elementen mit FlowRow oder FlowColumn verwenden, um ein gitterartiges Layout zu erstellen. Dieser Ansatz eignet sich zum Erstellen responsiver Layouts, die sich an die Größe Ihres Geräts anpassen.

Es gibt einige Beispiele dafür, was Sie mit Gewichten erreichen können. Ein Beispiel ist ein Raster, in dem die Elemente gleich groß sind, wie unten dargestellt:

Raster mit Flusszeile erstellt
Abbildung 3 Raster mit FlowRow erstellen

So erstellen Sie ein Raster mit gleich großen Elementen:

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

Wenn Sie einen weiteren Artikel hinzufügen und ihn zehn statt neunmal wiederholen, nimmt der letzte Artikel die gesamte letzte Spalte ein, da das Gesamtgewicht für die gesamte Zeile 1f beträgt:

Letztes Element in voller Größe im Raster
Abbildung 4: Mit FlowRow ein Raster erstellen, bei dem das letzte Element die volle Breite einnimmt

Sie können Gewichte mit anderen Modifiers kombinieren, z. B. mit Modifier.width(exactDpAmount), Modifier.aspectRatio(aspectRatio) oder Modifier.fillMaxWidth(fraction). Diese Modifikatoren wirken zusammen, um eine responsive Größe von Elementen innerhalb einer FlowRow (oder FlowColumn) zu ermöglichen.

Sie können auch ein abwechselndes Raster mit verschiedenen Artikelgrößen erstellen, bei dem zwei Artikel jeweils die Hälfte der Breite einnehmen und ein Artikel die volle Breite der nächsten Spalte einnimmt:

Wechselraster mit Flusszeile
Abbildung 5. FlowRow mit Zeilen unterschiedlicher Größe

Das geht mit dem folgenden Code:

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

Fraktionelle Größenanpassung

Mit Modifier.fillMaxWidth(fraction) können Sie die Größe des Containers angeben, den ein Artikel einnehmen soll. Das unterscheidet sich von der Funktionsweise von Modifier.fillMaxWidth(fraction), wenn es auf Row oder Column angewendet wird, da Row/Column-Elemente einen Prozentsatz der verbleibenden Breite einnehmen, anstatt die gesamte Breite des Containers.

Der folgende Code liefert beispielsweise unterschiedliche Ergebnisse, wenn FlowRow oder Row verwendet wird:

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: Mittleres Element mit einem Anteil von 0,7 an der gesamten Containerbreite.

Bruchteilbreite mit Flusszeile

Row: Mittleres Element nimmt 0,7 % der verbleibenden Row-Breite ein.

Bruchteilbreite mit Zeile

fillMaxColumnWidth() und fillMaxRowHeight()

Wenn Sie Modifier.fillMaxColumnWidth() oder Modifier.fillMaxRowHeight() auf ein Element in einem FlowColumn oder FlowRow anwenden, haben alle Elemente in derselben Spalte oder Zeile dieselbe Breite oder Höhe wie das größte Element in der Spalte oder Zeile.

In diesem Beispiel wird FlowColumn beispielsweise verwendet, um die Liste der Android-Desserts anzuzeigen. Sie sehen den Unterschied in der Breite der einzelnen Elemente, wenn Modifier.fillMaxColumnWidth() auf die Elemente angewendet wird, und wenn dies nicht der Fall ist und die Elemente umgebrochen werden.

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() wurde auf jedes Element angewendet

fillMaxColumnWidth

Es wurden keine Änderungen an der Breite festgelegt (Elemente werden umgebrochen)

Es wurde keine maximale Spaltenbreite für die Füllung festgelegt.