Compose 中的 ConstraintLayout

ConstraintLayout 可協助依據螢幕上的其他物件放置可組合元件,也能取代一般使用多個巢狀 RowColumnBox 和自訂布局元素的做法。當 實作 較 大的 配置 有 較 複雜 的 對準 要求 時, ConstraintLayout 很有用。

欲 在 Compose 中 使用 ConstraintLayout, 您 必須 在 build.gradle 中 加入 這個 依附元件:

implementation "androidx.constraintlayout:constraintlayout-compose:1.0.0-beta02"
增進 可讀性 和 維護性。

Compose 中的 ConstraintLayout 適用於 DSL

  • 參考 是 為了 使用 createRefs()createRefFor() 而建立的, 而且 ConstraintLayout 中的 每個 可組合 都需要 相關的 參考資料。
  • 限制條件是透過 constrainAs() 修飾元提供,該修飾元會經由參數接收參照資訊,並讓您在 lambda 主體中指定其限制條件。
  • 限制條件是透過 linkTo() 或其他實用方法指定。
  • parent 是 現有 參考資料, 可 用來 指定 ConstraintLayout 可組合 本身 所適用的 限制。

以下 是 使用 ConstraintLayout 的 可組合 範例:

@Composable
fun ConstraintLayoutContent() {
    ConstraintLayout {
        // Create references for the composables to constrain
        val (button, text) = createRefs()

        Button(
            onClick = { /* Do something */ },
            // Assign reference "button" to the Button composable
            // and constrain it to the top of the ConstraintLayout
            modifier = Modifier.constrainAs(button) {
                top.linkTo(parent.top, margin = 16.dp)
            }
        ) {
            Text("Button")
        }

        // Assign reference "text" to the Text composable
        // and constrain it to the bottom of the Button composable
        Text("Text", Modifier.constrainAs(text) {
            top.linkTo(button.bottom, margin = 16.dp)
        })
    }
}

這個 程式碼 會限制 Button 的上層 與 16.dp 形成 邊界, 然後 Text 連結 Button 的 底部 也與 16.dp 形成 邊界。

顯示 ConstraintLayout 中配置好的按鈕和文字元素

Decoupled API

ConstraintLayout 範例中,限制條件是以內嵌方式指定,並在其套用目標的可組合元件中設定了修飾元。但是, 在某些情況下, 最好 將限制 與 所套用的 配置 分離。 舉例來說,您可能需要根據螢幕設定修改限制條件,或是在兩組限制條件之間產生動畫動作。

對於 這樣 的案例, 您 可以 透過 不同 方式 使用 ConstraintLayout

  1. ConstraintSet 做為 參數 傳遞 給 ConstraintLayout
  2. 指派 在 ConstraintSet 創建的 參考, 以 組合 使用 layoutId 調節器。
@Composable
fun DecoupledConstraintLayout() {
    BoxWithConstraints {
        val constraints = if (minWidth < 600.dp) {
            decoupledConstraints(margin = 16.dp) // Portrait constraints
        } else {
            decoupledConstraints(margin = 32.dp) // Landscape constraints
        }

        ConstraintLayout(constraints) {
            Button(
                onClick = { /* Do something */ },
                modifier = Modifier.layoutId("button")
            ) {
                Text("Button")
            }

            Text("Text", Modifier.layoutId("text"))
        }
    }
}

private fun decoupledConstraints(margin: Dp): ConstraintSet {
    return ConstraintSet {
        val button = createRefFor("button")
        val text = createRefFor("text")

        constrain(button) {
            top.linkTo(parent.top, margin = margin)
        }
        constrain(text) {
            top.linkTo(button.bottom, margin)
        }
    }
}

然後, 如果 您 需要 變更 限制, 只要 傳遞 不同的 ConstraintSet 即可。

瞭解詳情

想進一步瞭解 Compose 中的 ConstraintLayout,請參閱使用 ConstraintLayout 的 Compose 範例一文提供的 API 應用。