こんなKotlin DSLの使い方あるんだと思って知らなかったのでメモしておきます。
Jetpack ComposeのConstraintLayoutは現状では、以下のように書きます。
ConstraintLayout {
val (text1, text2, text3) = createRefs()
Text("Text1", Modifier.constrainAs(text1) {
start.linkTo(text2.end, margin = 20.dp)
})
Text("Text2", Modifier.constrainAs(text2) {
centerTo(parent)
})
val barrier = createBottomBarrier(text1, text2)
Text("This is a very long text", Modifier.constrainAs(text3) {
top.linkTo(barrier, margin = 20.dp)
centerHorizontallyTo(parent)
width = Dimension.preferredWrapContent.atMost(40.dp)
})
}
以下のように Modifier.constrainAs()
を使うことができていますが、これがConstraintLayout以外のレイアウトでも見えてしまうと全く関係ないレイアウトでConstraintLayoutのプロパティが使えてカオスになってしまいます。しかもModifierはただのinterfaceです。どのようにしているのでしょうか?
ConstraintLayout {
val (text1, text2, text3) = createRefs()
Text("Text1", Modifier.constrainAs(text1) {
start.linkTo(text2.end, margin = 20.dp)
})
最初にコードで結論を書くと以下のように、スコープを作って、interfaceを継承したcompanion objectを実装して、companion objectとインスタンス両方に対しての拡張関数を定義して、そのスコープから呼び出してあげることで、スコープ内のみでしかCompanion objectのメソッドも使えないようにすることができます。
interface MyModifier {
companion object : MyModifier
}
class MyLayoutScope {
fun MyModifier.myCustomMethod() {
}
}
fun myLayout() {
MyLayoutScope().apply {
MyModifier.myCustomMethod()
}
}
以下どうやっているかのメモ
ConstraintLayout{}
で ConstraintLayoutScopeのブロックを渡させる。
@Composable
fun ConstraintLayout(
modifier: Modifier = Modifier,
children: @Composable ConstraintLayoutScope.() -> Unit
) {
ConstraintLayoutScopeにはModifier.constrainAs()という拡張関数がある。
拡張関数だけではModifier.constrainAs()
のようにstatic methodの呼び出しのようにすることはできないのを以下のようにinterfaceを継承したcompanion objectを実装することで可能にしている。
@Stable
interface Modifier {
...
companion object : Modifier {