Compose 中的流程版面配置

FlowRowFlowColumn 是與 RowColumn 類似的可組合項,但在容器用盡空間時,這些項目會流入下一行。這項作業會建立多個資料列或資料欄。設定 maxItemsInEachRowmaxItemsInEachColumn 也可以控制系列中的委刊項數量。您通常可以使用 FlowRowFlowColumn 建構回應式版面配置,因為當項目為某個尺寸過大時,內容不會遭到截斷,而將 maxItemsInEach*Modifier.weight(weight) 搭配使用,也有助於建構可在需要時填滿/展開列或欄寬度的版面配置。

方塊或篩選 UI 的常見例子為:

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

這段程式碼會產生上述的 UI,當第一列空間不足時,項目會自動移至下一列。

流程版面配置的功能

流程版面配置包含下列功能和屬性,可用於在應用程式中建立不同的版面配置。

主軸排列方式:水平或垂直排列

主軸是項目版面配置的軸 (例如在 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 中,這是垂直軸。如要變更容器內整體內容在交叉軸中的排列方式,請使用 verticalArrangement 代表 FlowRow,並將 horizontalArrangement 用於 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 項目會佔據剩餘寬度的百分比,而非整個容器寬度。

舉例來說,以下程式碼會在使用 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

未設定寬度變更 (包裝項目)

未設定任何填滿資料欄寬度上限