Compose 中的流式布局

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

一个典型示例是用于芯片或过滤界面:

FlowRow 中的 5 个芯片,显示在没有更多可用空间时溢出到下一行。
图 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)

按开始时间排列的商品

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

容器中心布局

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

单个商品的对齐方式

您可能希望以不同的对齐方式定位行中的各个项。这与 verticalArrangementhorizontalArrangement 不同,因为它会使项目在当前行内对齐。您可以使用 Modifier.align() 应用此效果。

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

FlowRow 设置了垂直对齐方式

结果

Alignment.Top (Default)

内容项靠上对齐

Alignment.Bottom

内容项底部对齐

Alignment.CenterVertically

内容项居中对齐

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

行或列中的商品数量上限

参数 maxItemsInEachRowmaxItemsInEachColumn 用于定义主轴上允许在一行中显示的最大项数,超过此数量后会换到下一行。默认值为 Int.MAX_INT,表示允许尽可能多的项,只要这些项的大小允许它们放入相应行中即可。

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

未设置上限

maxItemsInEachRow = 3

未在流量行上设置上限 在流程行中设置了内容数量上限

商品重量

权重会根据商品及其放置行中的可用空间来增加商品。重要的是,FlowRowRow 在使用权重计算商品宽度方面存在差异。对于 Rows,权重基于 Row 中的所有商品。使用 FlowRow 时,权重取决于商品所在行的商品,而不是 FlowRow 容器中的所有商品。

例如,如果您有 4 个商品都位于一条线上,且每个商品的权重各不相同,分别为 1f, 2f, 1f3f,则总权重为 7f。行或列中的剩余空间将除以 7f。然后,使用以下公式计算每个商品的宽度:weight * (remainingSpace / totalWeight)

您可以将 Modifier.weight 和 max items 与 FlowRowFlowColumn 结合使用,以创建网格状布局。此方法有助于创建可根据设备尺寸进行调整的自适应布局。

以下是一些示例,展示了如何使用权重来实现不同的效果。一个示例是项目大小相等的网格,如下所示:

使用流式布局行创建的网格
图 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) 应用于 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。

具有流式布局的行中的分数宽度

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

带行的分数宽度

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

未设置宽度变化(换行项)

未设置“不填充最大列宽”