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,项是水平排列的)。horizontalArrangement FlowRow 中的参数用于控制可用空间在内容之间的分配方式。

下表显示了针对项设置 horizontalArrangement 的示例 对于 FlowRow

已在“FlowRow”中设置水平排列

结果

Arrangement.Start (Default)

以开头方式排列的项

Arrangement.SpaceBetween

内容排列方式(中间有空格)

Arrangement.Center

排列在中央的物品

Arrangement.End

已排列在末尾的项

Arrangement.SpaceAround

周围有一定空间的摆放项

Arrangement.spacedBy(8.dp)

按特定 dp 间隔的项

对于 FlowColumnverticalArrangement 提供了类似的选项,其中 默认为 Arrangement.Top

交叉轴排列

交叉轴是与主轴相反方向的轴。对于 例如,在 FlowRow 中,这是纵轴。要更改总体的 容器内的内容按交叉轴排列,使用 verticalArrangement - FlowRowhorizontalArrangement - FlowColumn

下表显示了针对 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

流行未设置上限 流行中设置的项数上限

延迟加载流项

ContextualFlowRowContextualFlowColumnFlowRowFlowColumn 的版本,可让您延迟加载内容 数据流行或列的位置。还可以提供有关商品位置的信息 (索引、行号和可用大小),例如,如果项位于第一个 。这对于大型数据集和上下文信息非常有用 。

maxLines 参数会限制显示的行数,overflow 参数指定在发生商品溢出时应显示的内容 从而允许您指定自定义expandIndicatorcollapseIndicator

例如,要显示“+”(剩余项目数)或“收起”按钮:

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

示例:上下文流行。
图 2. ContextualFlowRow 示例

商品重量

权重可根据项的系数和行的可用空间增加项 。重要的是,FlowRowRow 之间有差异 以及如何使用权重计算物品的宽度。对于 Rows,权重 基于 Row 中的所有项。使用 FlowRow 时,权重基于 包含某个项的专列项中的,而不是 FlowRow 容器。

例如,如果您有 4 个项目,它们全都在同一行上,并且每个项目都有不同的 分别为 1f, 2f, 1f3f,总权重为 7f。剩余空间 将除以 7f。然后,每个列表项的宽度 计算公式为:weight * (remainingSpace / totalWeight)

您可以将 Modifier.weight 和商品数上限功能与 FlowRowFlowColumn 创建类似于网格的布局。这种方法有助于创建 可适应设备尺寸的自适应布局。

下面通过几个不同的示例说明了使用权重可以实现的效果。一个 示例是一个网格,其中各项大小均等,如下所示:

使用流程行创建了网格
图 3. 使用 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

网格上最后一项完整尺寸的内容
图 4. 使用 FlowRow 创建最后一项占据全宽的网格

您可以将权重与其他 Modifiers 结合使用,例如 Modifier.width(exactDpAmount), Modifier.aspectRatio(aspectRatio),或者 Modifier.fillMaxWidth(fraction)。这些修饰符都与 支持对 FlowRow(或 FlowColumn)内的项进行自适应大小调整。

您还可以创建一个大小不同的交替网格,其中两个项目 每个元素占总宽度的一半,并且一个元素占下一个项目的全宽 列:

带流动行的交替网格
图 5. 具有交替大小的行大小的 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 项会占用剩余宽度的百分比,而不是 整个容器的宽度。

例如,使用 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%。

行宽的分数

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

未设置宽度更改(封装内容)

未设置填充最大列宽