Compose 中的流式布局

FlowRowFlowColumn 是类似于 RowColumn 的可组合项,但不同之处在于当容器空间不足时,项目会流式排列到下一行。这会创建多行或多列。行中的项目数量也可以通过设置 maxItemsInEachRowmaxItemsInEachColumn 来控制。您通常可以使用 FlowRowFlowColumn 构建响应式布局 - 如果项目对于某个维度过大,内容不会被截断,并且结合使用 maxItemsInEach*Modifier.weight(weight) 可以帮助构建在需要时填充/扩展行或列宽度的布局。

典型的例子是用于纸片或过滤界面

5 chips in a FlowRow, showing the overflow to the next line when there is no
more space available.
图 1. FlowRow 示例

基本用法

要使用 FlowRowFlowColumn,请创建这些可组合项并将应遵循标准流的项目放置在其中

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

此代码段会生成上面显示的界面,当第一行没有更多空间时,项目会自动流式排列到下一行。

流式布局的特性

流式布局具有以下特性和属性,您可以使用它们在应用中创建不同的布局。

主轴排列:水平或垂直排列

主轴是项目布局所在的轴(例如,在 FlowRow 中,项目是水平排列的)。FlowRow 中的 horizontalArrangement 参数控制可用空间在项目之间分配的方式。

下表显示了在 FlowRow 的项目上设置 horizontalArrangement 的示例

FlowRow 上设置的水平排列

结果

Arrangement.Start (Default)

Items arranged with start

Arrangement.SpaceBetween

Items arrangement with space in between

Arrangement.Center

Items arranged in the center

Arrangement.End

Items arranged at the end

Arrangement.SpaceAround

Items arranged with space around them

Arrangement.spacedBy(8.dp)

Items spaced by a certain dp

对于 FlowColumnverticalArrangement 也有类似的选项,默认为 Arrangement.Top

交叉轴排列

交叉轴是与主轴方向相反的轴。例如,在 FlowRow 中,这是垂直轴。要更改容器内整体内容在交叉轴上的排列方式,请对 FlowRow 使用 verticalArrangement,对 FlowColumn 使用 horizontalArrangement

对于 FlowRow,下表显示了在项目上设置不同 verticalArrangement 的示例

FlowRow 上设置的垂直排列

结果

Arrangement.Top (Default)

Container top arrangement

Arrangement.Bottom

Container bottom arrangement

Arrangement.Center

Container center arrangement

对于 FlowColumnhorizontalArrangement 也有类似的选项。默认的交叉轴排列为 Arrangement.Start

单个项目对齐

您可能希望以不同的对齐方式定位行中的单个项目。这与 verticalArrangementhorizontalArrangement 不同,因为它会对齐当前行中的项目。您可以使用 Modifier.align() 来实现这一点。

例如,当 FlowRow 中的项目高度不同时,行会采用最高项目的高度,并对项目应用 Modifier.align(alignmentOption)

FlowRow 上设置的垂直对齐方式

结果

Alignment.Top (Default)

Items aligned to the top

Alignment.Bottom

Items aligned to the bottom

Alignment.CenterVertically

Items aligned to the center

对于 FlowColumn,也有类似的选项。默认对齐方式为 Alignment.Start

行或列中的最大项目数

参数 maxItemsInEachRowmaxItemsInEachColumn 定义了主轴中每行允许的最大项目数,超过该数量则会换行。默认值为 Int.MAX_INT,它允许尽可能多的项目,只要它们的大小允许它们适合该行。

例如,设置 maxItemsInEachRow 会强制初始布局只包含 3 个项目

未设置最大值

maxItemsInEachRow = 3

No max set on flow row Max items set on flow row

项目权重

权重会根据项目的因子及其所在行中的可用空间来增加项目。重要的是,FlowRowRow 在计算项目宽度时,使用权重的方式有所不同。对于 Rows,权重是基于 Row所有项目的。对于 FlowRow,权重是基于项目所在行中的项目,而不是 FlowRow 容器中的所有项目。

例如,如果您有 4 个项目都在一行中,它们的权重分别为 1f、2f、1f3f,则总权重为 7f。行或列中的剩余空间将除以 7f。然后,每个项目宽度将使用以下公式计算:weight * (remainingSpace / totalWeight)

您可以结合使用 Modifier.weight 和最大项目数与 FlowRowFlowColumn 来创建类似网格的布局。这种方法对于创建适应设备尺寸的响应式布局非常有用。

您可以使用权重实现几种不同的效果。一个示例是项目大小相等的网格,如下所示

Grid created with flow row
图 2. 使用 FlowRow 创建网格

要创建相等项目大小的网格,您可以执行以下操作

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

重要的是,如果您添加另一个项目并重复它 10 次而不是 9 次,最后一个项目将占据整个最后一列,因为整行的总权重为 1f

Last item full size on grid
图 3. 使用 FlowRow 创建网格,其中最后一个项目占据完整宽度

您可以将权重与其他 Modifiers 结合使用,例如 Modifier.width(exactDpAmount)Modifier.aspectRatio(aspectRatio)Modifier.fillMaxWidth(fraction)。这些修饰符协同工作,允许在 FlowRow(或 FlowColumn)中实现项目的响应式大小调整。

您还可以创建不同项目大小的交替网格,其中两个项目各占一半宽度,一个项目占下一列的全部宽度

Alternating grid with flow row
图 4. FlowRow,行的大小交替

您可以使用以下代码实现此目的

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

分数大小

使用 Modifier.fillMaxWidth(fraction),您可以指定项目应占用的容器大小。这与 Modifier.fillMaxWidth(fraction) 应用于 RowColumn 时的工作方式不同,Row/Column 项目占用剩余宽度的百分比,而不是整个容器的宽度。

例如,在使用 FlowRowRow 时,以下代码会产生不同的结果

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:中间项目占整个容器宽度的 0.7 比例。

Fractional width with flow row

Row:中间项目占用剩余 Row 宽度的 0.7%。

Fractional width with row

fillMaxColumnWidth()fillMaxRowHeight()

Modifier.fillMaxColumnWidth()Modifier.fillMaxRowHeight() 应用于 FlowColumnFlowRow 中的项目,可确保同一列或行中的项目与该列/行中最大项目具有相同的宽度或高度。

例如,此示例使用 FlowColumn 显示 Android 甜点列表。您可以看到当 Modifier.fillMaxColumnWidth() 应用于项目时,与未应用且项目换行时,每个项目宽度的差异。

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

fillMaxColumnWidth

未设置宽度更改(换行项目)

No fill max column width set