Compose の ConstraintLayout

ConstraintLayout は、コンポーザブルを画面上の他の要素に対し相対的に配置するのに役立ちます。複数のネストされた RowColumnBox 要素やカスタムのレイアウト要素を使用する代わりの手段です。ConstraintLayout は、配置要件がより複雑な、大規模なレイアウトを実装する場合に便利です。

Compose で ConstraintLayout を使用するには、build.gradle に次の依存関係を追加する必要があります。

implementation "androidx.constraintlayout:constraintlayout-compose:1.0.0-beta02"

Compose の ConstraintLayoutDSL で機能します。

  • 参照は createRefs() または createRefFor() を使用して作成されます。ConstraintLayout 内の各コンポーザブルは、関連付けられた参照を持つ必要があります。
  • 制約は、参照をパラメータとして受け取る constrainAs() 修飾子を使用して指定します。この修飾子により、本文のラムダで制約を指定できます。
  • 制約は、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 に制約し、TextButton の底部に対しマージン 16.dp に制約します。

ConstraintLayout で配置されたボタンとテキスト要素を示しています

ConstraintLayout のその他の使用例については、レイアウトの Codelab をご覧ください。

API の分離

ConstraintLayout の例では、制約はインラインで指定され、適用対象のコンポーザブルで修飾子が付けられていました。しかし、適用対象のレイアウトから制約を分離した方がよい場合もあります。たとえば、画面構成に基づいて制約を変更する場合や、2 つの制約セットの間でアニメーション化を行う場合があります。

このような場合は、別の方法で 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 を渡すだけで済みます。

詳細

Jetpack Compose でのレイアウトの Codelab にある Constraint Layout のセクションで、Compose の ConstraintLayout の詳細をご覧になり、ConstraintLayout を使用する Compose サンプルで API の実際の動作をご確認ください。