Ablauflayouts in Compose

FlowRow und FlowColumn sind zusammensetzbare Funktionen, die Row und Column ähneln, sich jedoch darin unterscheiden, dass Elemente in die nächste Zeile fließen, wenn der Container nicht mehr genügend Platz hat. Dadurch werden mehrere Zeilen oder Spalten erstellt. Die Anzahl der Elemente in einer Zeile kann auch durch Festlegen von maxItemsInEachRow oder maxItemsInEachColumn gesteuert werden. Sie können häufig FlowRow und FlowColumn verwenden, um responsive Layouts zu erstellen. Inhalte werden nicht abgeschnitten, wenn Elemente für eine Dimension zu groß sind. Wenn Sie maxItemsInEach* mit Modifier.weight(weight) kombinieren, können Sie Layouts erstellen, die die Breite einer Zeile oder Spalte bei Bedarf ausfüllen oder erweitern.

Das typische Beispiel zeigt einen Chip oder eine 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 Nutzung

Wenn Sie FlowRow oder FlowColumn verwenden möchten, erstellen Sie diese zusammensetzbaren Funktionen und platzieren Sie die Elemente darin, die dem Standardablauf entsprechen sollten:

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

Durch dieses Snippet wird die oben gezeigte UI angezeigt. Elemente fließen automatisch in die nächste Zeile, wenn in der ersten Zeile kein Platz mehr vorhanden ist.

Features des Ablauflayouts

Ablauflayouts 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, auf der Elemente angeordnet werden. In FlowRow sind Elemente beispielsweise horizontal angeordnet. Der Parameter horizontalArrangement in FlowRow steuert, wie freier Speicherplatz auf die Elemente verteilt wird.

Die folgende Tabelle zeigt Beispiele für die Festlegung von horizontalArrangement für Elemente für FlowRow:

Horizontale Anordnung auf FlowRow festgelegt

Ergebnis

Arrangement.Start (Default)

Artikel nach Anfang angeordnet

Arrangement.SpaceBetween

Anordnung von Elementen mit Abstand dazwischen

Arrangement.Center

Artikel in der Mitte angeordnet

Arrangement.End

Elemente am Ende angeordnet

Arrangement.SpaceAround

Gegenstände in um sie herum angeordnetem Platz

Arrangement.spacedBy(8.dp)

Elemente mit einem bestimmten dp-Abstand

Für FlowColumn sind ähnliche Optionen mit verticalArrangement verfügbar. Der Standardwert 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 die gesamten Inhalte im Container in der Kreuzachse angeordnet sind, verwenden Sie verticalArrangement für FlowRow und horizontalArrangement für FlowColumn.

Die folgende Tabelle zeigt Beispiele für die Festlegung unterschiedlicher verticalArrangement für die Elemente für FlowRow:

Vertikale Anordnung auf FlowRow festgelegt

Ergebnis

Arrangement.Top (Default)

Anordnung des Containers oben

Arrangement.Bottom

Behälter Bodenanordnung

Arrangement.Center

Container Center-Anordnung

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

Ausrichtung einzelner Elemente

Es kann sinnvoll sein, einzelne Elemente mit unterschiedlichen Ausrichtungen innerhalb der Zeile zu positionieren. Dies unterscheidet sich von verticalArrangement und horizontalArrangement, da hier die Elemente innerhalb der aktuellen Zeile ausgerichtet werden. Sie können dies mit Modifier.align() anwenden.

Wenn beispielsweise Elemente in einem FlowRow unterschiedliche Höhen haben, nimmt die Zeile die Höhe des größten Elements und wendet Modifier.align(alignmentOption) auf die Elemente an:

Vertikale Ausrichtung auf FlowRow festgelegt

Ergebnis

Alignment.Top (Default)

Elemente, die oben ausgerichtet sind

Alignment.Bottom

Elemente unten ausgerichtet

Alignment.CenterVertically

Elemente mittig ausgerichtet

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

Maximale Anzahl der Elemente in Zeile oder Spalte

Die Parameter maxItemsInEachRow oder maxItemsInEachColumn definieren die maximale Anzahl von Elementen in der Hauptachse, die in einer Zeile zulässig sind, bevor sie in die nächste gezogen werden. Die Standardeinstellung ist Int.MAX_INT, bei der so viele Elemente wie möglich zulässig sind, sofern sie aufgrund ihrer Größe in die Linie passen.

Wenn Sie beispielsweise einen maxItemsInEachRow festlegen, wird erzwungen, dass das anfängliche Layout nur drei Elemente enthält:

Kein Maximalwert festgelegt

maxItemsInEachRow = 3

Kein Maximalwert für Ablaufzeile festgelegt Maximale Anzahl von Elementen in Ablaufzeile festgelegt

Elemente für Lazy Loading

ContextualFlowRow und ContextualFlowColumn sind eine spezielle Version von FlowRow und FlowColumn, mit denen Sie die Inhalte Ihrer Ablaufzeile oder -spalte per Lazy Loading laden können. Sie liefern auch Informationen zur Position der Elemente (Index, Zeilennummer und verfügbare Größe), z. B. ob sich das Element in der ersten Zeile befindet. Dies ist nützlich bei großen Datensätzen und wenn Sie Kontextinformationen zu einem Element benötigen.

Der Parameter maxLines begrenzt die Anzahl der angezeigten Zeilen und der Parameter overflow gibt an, was angezeigt werden soll, wenn ein Überlauf von Elementen erreicht wird. So können Sie einen benutzerdefinierten Wert für expandIndicator oder collapseIndicator angeben.

So zeigen Sie beispielsweise die Schaltflächen "+ (Anzahl verbleibender Artikel)" oder "Weniger anzeigen" an:

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 kontextbezogene Ablaufzeilen
Abbildung 2: Beispiel für ContextualFlowRow

Artikelgewichte

Durch die Gewichtung wird ein Element basierend auf seinem Faktor und dem verfügbaren Platz auf der Linie vergrößert, in der es platziert wurde. Wichtig ist, dass es einen Unterschied zwischen FlowRow und Row gibt, wie die Gewichtung zur Berechnung der Breite eines Elements verwendet wird. Bei Rows basiert die Gewichtung auf allen Artikeln im Row. Bei FlowRow basiert die Gewichtung auf den Elementen der Werbebuchung, in der ein Element platziert wird, und nicht auf allen Elementen im Container FlowRow.

Wenn Sie beispielsweise vier Elemente haben, die alle auf eine Linie fallen und jeweils eine andere Gewichtung von 1f, 2f, 1f und 3f haben, beträgt die Gesamtgewichtung 7f. Der verbleibende Platz in einer Zeile oder Spalte wird durch 7f geteilt. Anschließend wird jede Elementbreite so berechnet: weight * (remainingSpace / totalWeight).

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

Es gibt verschiedene Beispiele dafür, was Sie mit Gewichtungen erreichen können. Ein Beispiel ist ein Raster, in dem Elemente die gleiche Größe haben, wie unten gezeigt:

Raster mit Ablaufzeile erstellt
Abbildung 3: FlowRow zum Erstellen eines Rasters verwenden

So erstellen Sie ein Raster gleicher Artikelgröße:

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

Wichtig: Wenn Sie ein weiteres Element hinzufügen und es 10-mal statt 9-mal wiederholen, nimmt das letzte Element die gesamte letzte Spalte ein, da die Gesamtgewichtung für die gesamte Zeile 1f beträgt:

Letztes Element in Originalgröße im Raster
Abbildung 4: Verwenden Sie FlowRow, um ein Raster zu erstellen, bei dem das letzte Element die volle Breite einnimmt.

Sie können Gewichtungen mit anderen Modifiers-Elementen wie Modifier.width(exactDpAmount), Modifier.aspectRatio(aspectRatio) oder Modifier.fillMaxWidth(fraction) kombinieren. Alle diese Modifikatoren dienen in Kombination, um eine responsive Größenanpassung von Elementen in einem FlowRow (oder FlowColumn) zu ermöglichen.

Sie können auch ein abwechselndes Raster mit unterschiedlichen Elementgrößen erstellen, wobei zwei Elemente jeweils die Hälfte der Breite einnehmen und ein Element die volle Breite der nächsten Spalte einnimmt:

Sich abwechselndes Raster mit Ablaufzeile
Abbildung 5. FlowRow mit abwechselnder Zeilengröße

Dazu können Sie den folgenden Code verwenden:

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

Teilgrößenanpassung

Mit Modifier.fillMaxWidth(fraction) können Sie die Größe des Containers angeben, den ein Element einnehmen soll. Dies unterscheidet sich von der Funktionsweise von Modifier.fillMaxWidth(fraction) bei Anwendung auf Row oder Column, da Row/Column-Elemente einen Prozentsatz der verbleibenden Breite und nicht die gesamte Breite des Containers einnehmen.

Der folgende Code führt beispielsweise bei der Verwendung von FlowRow im Vergleich zu Row zu unterschiedlichen Ergebnissen:

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 0,7-Anteil der gesamten Containerbreite.

Bruchbreite mit Ablaufzeile

Row: Das mittlere Element nimmt 0,7 % der verbleibenden Breite von Row ein.

Bruchbreite mit Zeile

fillMaxColumnWidth() und fillMaxRowHeight()

Durch die Anwendung von Modifier.fillMaxColumnWidth() oder Modifier.fillMaxRowHeight() auf ein Element in einem FlowColumn- oder FlowRow-Element wird sichergestellt, dass Elemente in derselben Spalte oder Zeile dieselbe Breite oder Höhe wie das größte Element in der Spalte/Zeile einnehmen.

In diesem Beispiel wird beispielsweise FlowColumn verwendet, um die Liste der Android-Desserts aufzurufen. Sie können die unterschiedlichen Breiten der einzelnen Elemente sehen, wenn Modifier.fillMaxColumnWidth() auf die Elemente angewendet wird und wenn dies nicht der Fall ist und die Elemente zusammengefasst 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() auf jedes Element angewendet

FillMaxColumnWidth

Keine Breitenänderungen festgelegt (Elemente umbrechen)

Keine Spaltenbreite für „Füllung“ festgelegt