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 的間距

如果是 FlowColumnverticalArrangement 也提供類似選項,預設值為 Arrangement.Top

跨軸排列

交叉軸是與主軸相反方向的軸。舉例來說,在 FlowRow 中,這是指縱軸。如要變更容器內整體內容在交叉軸上的排列方式,請針對 FlowRow 使用 verticalArrangement,針對 FlowColumn 使用 horizontalArrangement

對於 FlowRow,下表顯示了對項目設定不同 verticalArrangement 的範例:

已設定FlowRow的垂直排列方式

結果

Arrangement.Top (Default)

容器頂端排列方式

Arrangement.Bottom

容器底部排列方式

Arrangement.Center

容器中心排列方式

如果是 FlowColumn,則可使用 horizontalArrangement 提供的類似選項。預設的交叉軸排列方式為 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) 指定項目應佔用的容器大小。這與套用至 RowColumnModifier.fillMaxWidth(fraction) 的運作方式不同,因為 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

未設定寬度變更 (納入項目)

未設定填滿上限欄寬