Modifier修飾子を見る(1)

参考

Modifier修飾子とは
コンポーザブルのサイズ、レイアウト、動作、外観を変更する
ユーザー補助ラベルなどの情報を追加する
ユーザー入力を処理する
要素をクリック可能、スクロール可能、ドラッグ可能、ズーム可能にするなど、高レベルの操作を追加する
Modifierを介して色々Viewをカスタマイズできる。

順序が重要
padding -> clickable
Box(
Modifier
.padding(100.dp)
.clickable { }
.background(Color.Red)
.fillMaxSize()
)
padding後にclickableを設定しているため、クリックできる範囲は赤いの範囲のみ
clickable -> padding
Box(
Modifier
.clickable { }
.padding(100.dp)
.background(Color.Red)
.fillMaxSize()
)
先頭の修飾子からレイアウトに影響を与えていると考えれば納得できる。

paddingFromBaseline
Textのbaselineからのpaddingを設定できるらしい。
実験してみた。
Text(
text = "Hoge",
color = Color.White,
modifier = Modifier
.background(Color.Blue)
.size(400.dp)
.paddingFromBaseline(top = 100.dp) // [1] ベースから上にpaddingを100dp設定
)
//
Box(
modifier = Modifier
.size(100.dp)
.border(BorderStroke(1.dp, Color.Red)) // [2] 100dpのBoxと重ねてbaselineからpaddingが出ているか確認
)
想定した通りになっていた。

スコープの安全性
@Composable
fun BoxTest() {
Box(
modifier = Modifier
.size(100.dp)
.border(BorderStroke(1.dp, Color.Red))
.align(Alignment.Center) // [1] エラー
)
}
Modifier.alignはBoxScopeの中でしか呼び出せないため、エラーになっている。
他にもColumnScopeやRowScopeなどがある。
@LayoutScopeMarker
@Immutable
interface BoxScope {
/**
* Pull the content element to a specific [Alignment] within the [Box]. This alignment will
* have priority over the [Box]'s `alignment` parameter.
*/
@Stable
fun Modifier.align(alignment: Alignment): Modifier
なぜScopeが存在するか?
AndroidViewで色々なパラメータをViewに設定してみて反映しないとなったことがある人は多いはず。
そういうトライ&エラーの時間を短くするためにScopeという概念ができた。

修飾子の抽出と再利用
頻繁に利用する修飾子を抽出し再利用することはコードベースの削減につながる。
抽出するときに気をつける点
(1)Scope設定された修飾子を使っている場合 → 制約あり
(2)Scope設定された修飾子を使っていない場合 → 制約なし
制約:抽出されてスコープ設定された修飾子は、同じスコープの直接の子にのみ渡す必要があります。
例えばModifier#weightはColumScope内でしか利用できない。
なのでその場合の抽出と再利用の仕方は以下のようになる。(ドキュメントには書いていないため正しいかはわからない)
そのScopeの拡張関数として定義する。
@Composable
fun ColumnScope.weight1FModifier(): Modifier {
return Modifier.weight(1F, false)
}
@Composable
fun BoxTest() {
Column {
Text("Hoge", modifier = weight1FModifier())
Text("Piyo", modifier = weight1FModifier())
}
}

.then()で修飾子チェーンを合体
再利用したい修飾子を少しカスタマイズしたい場合に使える。
@Composable
fun ColumnScope.weight1FModifier(): Modifier {
return Modifier.weight(1F, false)
}
@Composable
fun BoxTest() {
Column {
Text("Hoge", modifier = Modifier
.background(Color.Red)// [1] Hogeの方は背景を赤くしたい
.then(weight1FModifier())) // [2] 再利用可能としたModifierで均等なサイズにしたい
Text("Piyo", modifier = weight1FModifier())
}
}
Hogeだけ赤くなっている。
注意点:修飾子は順番が大切なため、modifier1.then(modifier2)
とmodifier2.then(modifier1)
で結果が変わる可能性がある