自訂版面配置

在 Compose 中,UI 元素是以發出 UI 的一部分,然後會新增至顯示於 。每個 UI 元素都有一個父項,而且可能有多個子項。每項 元素也位於其父項中,並指定為 (x, y) 位置; 大小,指定為 widthheight

父項會定義其子項元素的限制。元素會要求 超出這些限制的大小限制條件會用於限制 每個元素的 widthheight 上限。如果元素含有子元素, 可能會測量每個子項以決定其大小。元素判定 就能定義子項 請參閱建立自訂元件 版面配置

在 UI 樹狀結構中完成每個節點的版面配置需要三個步驟。每個節點都必須:

  1. 測量任何子項
  2. 決定其大小
  3. 放置其子項

節點版面配置的三個步驟:測量子項、決定大小、放置子項

使用範圍可定義「何時」可以測量及放置子項。 只有在測量和版面配置傳遞期間才能測量版面配置 且子項只能在版面配置期間 (且 )。由於 Compose 範圍的關係, MeasureScope、 和 PlacementScope, 這會在編譯期間強制執行

使用版面配置修飾符

您可以使用 layout 修飾符修改元素的測量及配置方式 。Layout 是 lambda;參數包括您可以測量的元素 以 measurable 的形式傳遞,然後該可組合項的傳入限制, constraints。自訂版面配置修飾符的外觀如下:

fun Modifier.customLayoutModifier() =
    layout { measurable, constraints ->
        // ...
    }

假設在畫面中顯示 Text,並控制從頂端到頂端之間的距離 新增文字的基準線這正是 paddingFromBaseline 修飾符的作用,這裡我們會以例子實作此修飾符。 方法是使用 layout 修飾符,以手動方式將可組合項放置在 。以下是當 Text 頂端邊框間距設為 24.dp 時的預期行為:

顯示一般 UI 邊框間距 (設定元素之間的距離) 與文字邊框間距 (設定一個基準到下一個基準的距離) 之間的差異。

以下是產生此間距的程式碼:

fun Modifier.firstBaselineToTop(
    firstBaselineToTop: Dp
) = layout { measurable, constraints ->
    // Measure the composable
    val placeable = measurable.measure(constraints)

    // Check the composable has a first baseline
    check(placeable[FirstBaseline] != AlignmentLine.Unspecified)
    val firstBaseline = placeable[FirstBaseline]

    // Height of the composable with padding - first baseline
    val placeableY = firstBaselineToTop.roundToPx() - firstBaseline
    val height = placeable.height + placeableY
    layout(placeable.width, height) {
        // Where the composable gets placed
        placeable.placeRelative(0, placeableY)
    }
}

以下程式碼的運作方式:

  1. measurable lambda 參數中,您可以測量 Text 呼叫 measurable.measure(constraints) 來呈現可評估參數
  2. 透過呼叫 layout(width, height) 來指定可組合項的大小 方法,因此也會提供用於放置已包裝元素的 lambda。於 在本例中,這是上一個基準及新增頂端邊框間距的高度。
  3. 您可以呼叫,將包裝的元素放到畫面中。 placeable.place(x, y)。如果沒有放置包裝的元素,這些元素就不會放置。 顯示。y 位置與頂端邊框間距對應 - 文字的第一個基準線

如要驗證此設定是否正常運作,請在 Text 上使用修飾符:

@Preview
@Composable
fun TextWithPaddingToBaselinePreview() {
    MyApplicationTheme {
        Text("Hi there!", Modifier.firstBaselineToTop(32.dp))
    }
}

@Preview
@Composable
fun TextWithNormalPaddingPreview() {
    MyApplicationTheme {
        Text("Hi there!", Modifier.padding(top = 32.dp))
    }
}

多個文字元素預覽;其中一個顯示元素之間的一般邊框間距,另一個顯示一個基準到下一個基準的邊框間距

建立自訂版面配置

layout 修飾符只能變更呼叫的可組合函式。測量及版面配置 多個可組合函式,請改用 Layout 可組合函式。這個可組合函式 可讓您手動測量和調整子項的版面配置所有較高層級版面配置 例如 ColumnRow,是使用 Layout 可組合函式所建構。

我們來建立基本的 Column 版本。大部分自訂版面配置都會遵循以下格式 模式:

@Composable
fun MyBasicColumn(
    modifier: Modifier = Modifier,
    content: @Composable () -> Unit
) {
    Layout(
        modifier = modifier,
        content = content
    ) { measurables, constraints ->
        // measure and position children given constraints logic here
        // ...
    }
}

measurableslayout 修飾符類似,都是 需要測量,而 constraints 是父項的限制。 和先前的邏輯相同,MyBasicColumn 可像先前一樣實作 :

@Composable
fun MyBasicColumn(
    modifier: Modifier = Modifier,
    content: @Composable () -> Unit
) {
    Layout(
        modifier = modifier,
        content = content
    ) { measurables, constraints ->
        // Don't constrain child views further, measure them with given constraints
        // List of measured children
        val placeables = measurables.map { measurable ->
            // Measure each children
            measurable.measure(constraints)
        }

        // Set the size of the layout as big as it can
        layout(constraints.maxWidth, constraints.maxHeight) {
            // Track the y co-ord we have placed children up to
            var yPosition = 0

            // Place children in the parent layout
            placeables.forEach { placeable ->
                // Position item on the screen
                placeable.placeRelative(x = 0, y = yPosition)

                // Record the y co-ord placed up to
                yPosition += placeable.height
            }
        }
    }
}

子項可組合項受 Layout 限制條件的限制 (沒有 minHeight限制條件),而且其位置是根據 yPosition 先前的可組合函式

自訂可組合函式的運用方式如下:

@Composable
fun CallingComposable(modifier: Modifier = Modifier) {
    MyBasicColumn(modifier.padding(8.dp)) {
        Text("MyBasicColumn")
        Text("places items")
        Text("vertically.")
        Text("We've done it by hand!")
    }
}

多個文字元素在一個欄中上下堆疊。

版面配置方向

透過變更 LocalLayoutDirection 本機組合。

如果要在畫面中以手動方式放置可組合項,LayoutDirection 會是 是 layout 修飾符的 LayoutScopeLayout 可組合函式。

使用 layoutDirection 時,請使用 place 放置可組合函式。與 placeRelative敬上 方法,place 不會根據版面配置方向變更 (從左到右或從右到左)。

實際操作自訂版面配置

如要進一步瞭解版面配置和修飾符,請參閱: Compose 的基本版面配置、 自訂版面配置的實際運作情形 建立自訂版面配置的 Compose 範例

瞭解詳情

如要進一步瞭解 Compose 中的自訂版面配置,請參閱以下其他資源。

影片