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)

使用 start 排序的項目

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

容器中心排列方式

如果是 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) 指定項目應佔用的容器大小。這與 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() 套用至項目時,每個項目的寬度與未套用 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

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

未設定填滿欄寬上限