Compose 中的流式布局

FlowRowFlowColumnRowColumn 类似,但不同之处在于,当容器空间不足时,项会流入下一行。这会创建多行或多列。还可以控制一行中项目的数量 只需设置 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 参数用于控制可用空间在项之间分配的方式。

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

FlowRow 上设置了水平排列

结果

Arrangement.Start (Default)

按开始时间排列的项

Arrangement.SpaceBetween

项目之间有空格的排列方式

Arrangement.Center

排列在中央的物品

Arrangement.End

在末尾排列的项

Arrangement.SpaceAround

周围有一定空间的摆放项

Arrangement.spacedBy(8.dp)

按特定 dp 间隔的项

对于 FlowColumnverticalArrangement 提供了类似的选项,默认值为 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

流行未设置上限 在流程行上设置的项数上限

延迟加载流程项

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 和 max 项与 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

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

未设置填充最大列宽