Compose 中的流式布局

FlowRow 和 FlowColumn 是与 Row 和 Column 类似的可组合项,但不同之处在于,当容器空间不足时,内容会流入下一行。这会创建多行或多列。您还可以通过设置 maxItemsInEachRow 或 maxItemsInEachColumn 来控制一行中的项数。您通常可以使用 FlowRow 和 FlowColumn 构建自适应布局,如果内容项对于某个维度而言过大,内容也不会被截断;将 maxItemsInEach* 与 Modifier.weight(weight) 结合使用,有助于构建在需要时填充/扩展行或列宽度的布局。

典型示例是条状标签或过滤界面:

FlowRow 中的 5 个条状标签,显示在没有更多可用空间时溢出到下一行。
图 1. FlowRow
示例

基本用法

如需使用 FlowRow 或 FlowColumn,请创建这些可组合项,并将应遵循标准流程的项放入其中:

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

从开头开始排列的项

Arrangement.SpaceBetween

内容排列方式:中间留有间距

Arrangement.Center

商品排列在中心位置

Arrangement.End

内容排列在末尾

Arrangement.SpaceAround

内容项周围留有空间

Arrangement.spacedBy(8.dp)

项之间间隔特定 dp

对于 FlowColumn,verticalArrangement 提供了类似的选项,默认值为 Arrangement.Top。

交叉轴排列

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

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

已在FlowRow上设置垂直排列方式

结果

Arrangement.Top (Default)

容器顶部排列

Arrangement.Bottom

容器底部排列

Arrangement.Center

容器居中排列

对于 FlowColumn,horizontalArrangement 提供了类似的选项。 默认的交叉轴排列方式为 Arrangement.Start。

单项对齐

您可能希望在行中以不同的对齐方式放置各个项。这与 verticalArrangement 和 horizontalArrangement 不同,因为它会在当前行内对齐项。您可以使用 Modifier.align() 应用此设置。

例如,当 FlowRow 中的项具有不同的高度时,行会采用最大项的高度,并对这些项应用 Modifier.align(alignmentOption):

已在FlowRow上设置垂直对齐方式

结果

Alignment.Top (Default)

内容项顶部对齐

Alignment.Bottom

项已底部对齐

Alignment.CenterVertically

内容居中对齐

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

行或列中的最大项数

参数 maxItemsInEachRow 或 maxItemsInEachColumn 用于定义主轴中一行允许的最大项数,超出此数量后会换行。默认值为 Int.MAX_INT,它允许尽可能多的项,只要它们的大小允许它们放入行中。

例如,设置 maxItemsInEachRow 会强制初始布局仅包含 3 项内容:

未设置上限

maxItemsInEachRow = 3

未在流式行上设置最大值 在流程行中设置的项目数上限

商品重量

权重会根据项的系数及其所在行的可用空间来放大项。重要的是,FlowRow 和 Row 在如何使用权重计算项宽度方面存在差异。对于 Rows,权重基于 Row 中的所有项。使用 FlowRow 时,权重基于放置了某个项的行中的项,而不是 FlowRow 容器中的所有项。

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

您可以将 Modifier.weight 和最大项数与 FlowRow 或 FlowColumn 结合使用,以创建网格状布局。此方法对于创建可根据设备尺寸进行调整的自适应布局非常有用。

以下是您可以使用权重实现的一些不同示例。例如,在网格中,各项的大小相同,如下所示:

使用流式行创建的网格
图 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:

网格中的最后一项采用完整尺寸
图 3. 使用 FlowRow 创建网格,其中最后一项占据整个宽度

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

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

交替网格与流式行
图 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) 应用于 Row 或 Column 时的运作方式不同,因为 Row/Column 项会占用剩余宽度的百分比,而不是整个容器的宽度。

例如,使用 FlowRow 与 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:中间项,宽度为整个容器宽度的 0.7。

具有流行的分数宽度

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

使用 Row 的分数宽度

fillMaxColumnWidth()和fillMaxRowHeight()

将 Modifier.fillMaxColumnWidth() 或 Modifier.fillMaxRowHeight() 应用于 FlowColumn 或 FlowRow 中的项可确保同一列或行中的项占据与该列/行中最大项相同的宽度或高度。

例如,此示例使用 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

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

未设置“填充最大列宽”