修飾子を使用すると、コンポーザブルを装飾または拡張できます。修飾子では、次のようなことができます。
- コンポーザブルのサイズ、レイアウト、動作、外観を変更する
- ユーザー補助ラベルなどの情報を追加する
- ユーザー入力を処理する
- 要素をクリック可能、スクロール可能、ドラッグ可能、ズーム可能にするなど、高レベルの操作を追加する
修飾子は標準の Kotlin オブジェクトです。Modifier
クラス関数のいずれかを呼び出して修飾子を作成します。
@Composable private fun Greeting(name: String) { Column(modifier = Modifier.padding(24.dp)) { Text(text = "Hello,") Text(text = name) } }
こうした関数を連鎖させてコンポーズできます。
@Composable private fun Greeting(name: String) { Column( modifier = Modifier .padding(24.dp) .fillMaxWidth() ) { Text(text = "Hello,") Text(text = name) } }
上記のコードでは、さまざまな修飾子関数を一緒に使用しています。
padding
は、要素の周囲にスペースを挿入します。fillMaxWidth
は、コンポーザブルを親から与えられた最大幅に合わせて調整します。
コンポーザブルのすべてが modifier
パラメータを受け取り、その修飾子を UI を出力する最初の子に渡すようにすることをおすすめします。これにより、コードの再利用性が向上し、動作が予測可能で直感的になります。詳しくは、Compose API のガイドライン「要素が修飾子パラメータを受け取って準拠する」についての説明をご覧ください。
修飾子の順序の重要性
修飾子関数の順序は重要です。各関数は前の関数が返す Modifier
を変更するため、順序は最終結果に影響を与えます。次の例をご覧ください。
@Composable fun ArtistCard(/*...*/) { val padding = 16.dp Column( Modifier .clickable(onClick = onClick) .padding(padding) .fillMaxWidth() ) { // rest of the implementation } }
上記のコードでは、padding
修飾子が clickable
修飾子の後に適用されるため、周囲のパディングを含むエリア全体がクリック可能となります。修飾子の順序が逆の場合、padding
で追加されたスペースはユーザー入力に反応しません。
@Composable fun ArtistCard(/*...*/) { val padding = 16.dp Column( Modifier .padding(padding) .clickable(onClick = onClick) .fillMaxWidth() ) { // rest of the implementation } }
組み込み修飾子
Jetpack Compose には、コンポーザブルの装飾や拡張に役立つ組み込み修飾子のリストが用意されています。レイアウトの調整に使用する一般的な修飾子は次のとおりです。
padding
、size
Compose で提供されるレイアウトは、デフォルトでは子をラップしていますが、ただし、size
修飾子を使用してサイズを設定できます。
@Composable fun ArtistCard(/*...*/) { Row( modifier = Modifier.size(width = 400.dp, height = 100.dp) ) { Image(/*...*/) Column { /*...*/ } } }
指定したサイズがレイアウトの親の制約を満たさない場合、そのサイズが適用されないことがあります。親の制約に関係なくコンポーザブルのサイズを固定する必要がある場合は、requiredSize
修飾子を使用します。
@Composable fun ArtistCard(/*...*/) { Row( modifier = Modifier.size(width = 400.dp, height = 100.dp) ) { Image( /*...*/ modifier = Modifier.requiredSize(150.dp) ) Column { /*...*/ } } }
この例では、親 height
が 100.dp
に設定されていても、requiredSize
修飾子が優先されるため、Image
の高さは 150.dp
になっています。
親によって許可されているすべての高さを子レイアウトで埋めるには、fillMaxHeight
修飾子を追加します(Compose では、fillMaxSize
と fillMaxWidth
も提供されています)。
@Composable fun ArtistCard(/*...*/) { Row( modifier = Modifier.size(width = 400.dp, height = 100.dp) ) { Image( /*...*/ modifier = Modifier.fillMaxHeight() ) Column { /*...*/ } } }
要素全体にパディングを追加するには、padding
修飾子を設定します。
テキストのベースラインの上にパディングを追加して、レイアウトの上部からベースラインまで一定の距離を空ける場合は、paddingFromBaseline
修飾子を使用します。
@Composable fun ArtistCard(artist: Artist) { Row(/*...*/) { Column { Text( text = artist.name, modifier = Modifier.paddingFromBaseline(top = 50.dp) ) Text(artist.lastSeenOnline) } } }
オフセット
レイアウトを元の位置に対して相対的に配置するには、offset
修飾子を追加し、x 軸と y 軸でオフセットを設定します。オフセットは、正の数でも負の数でもかまいません。padding
と offset
の違いは、offset
をコンポーザブルに追加しても測定値自体は変更されない点です。
@Composable fun ArtistCard(artist: Artist) { Row(/*...*/) { Column { Text(artist.name) Text( text = artist.lastSeenOnline, modifier = Modifier.offset(x = 4.dp) ) } } }
offset
修飾子は、レイアウト方向に従って横方向に適用されます。正の offset
を設定する場合、左から右方向のコンテキストでは要素が右に移動し、右から左方向のコンテキストでは要素が左に移動します。レイアウト方向を考慮せずにオフセットを設定する必要がある場合は、absoluteOffset
修飾子をご覧ください。この修飾子を使用すると、正のオフセット値を設定した場合に要素が常に右へ移動します。
offset
修飾子には、オフセットをパラメータとして受け取る offset
と、ラムダで受け取る offset
という 2 つのオーバーロードがあります。それぞれのオーバーロードを使用すべき状況と、パフォーマンスを最適化する方法について詳しくは、Compose のパフォーマンス - 可能な限り読み取りを延期するをご覧ください。
Compose でのスコープの安全性
Compose には、特定のコンポーザブルの子に適用される場合にのみ使用できる修飾子があります。Compose はカスタム スコープによって、これを可能にしています。
たとえば、Box
サイズに影響を与えずに子を親の Box
と同じサイズにするには、matchParentSize
修飾子を使用します。matchParentSize
は BoxScope
でのみ使用できます。そのため、この修飾子を使用できるのは、親の Box
内の子に限られます。
スコープの安全性により、他のコンポーザブルやスコープでは機能しない修飾子を追加できなくなるため、試行錯誤する時間を節約できます。
スコープ修飾子は、子に関して親が知っておくべき情報を親に通知します。これは、一般に親データ修飾子とも呼ばれます。その内部構造は汎用修飾子とは異なりますが、使用方法の観点から見るとこれらの違いは重要ではありません。
matchParentSize
(Box
)
前述のように、Box
サイズに影響を与えずに子レイアウトを親 Box
と同じサイズにする場合は、matchParentSize
修飾子を使用します。
matchParentSize
は Box
スコープ内でしか使用できず、Box
コンポーザブルの直接の子にのみ適用されます。
以下の例では、子 Spacer
は親 Box
からサイズを取得し、親はそのサイズを最も大きな子(この場合は ArtistCard
)から取得しています。
@Composable fun MatchParentSizeComposable() { Box { Spacer( Modifier .matchParentSize() .background(Color.LightGray) ) ArtistCard() } }
matchParentSize
ではなく fillMaxSize
を使用した場合、Spacer
は親が使用できるすべてのスペースを使用します。この場合、親は使用可能なスペース全体に拡大します。
Row
と Column
の weight
前のセクションのパディングとサイズで説明したように、デフォルトでは、コンポーザブルのサイズは自身がラップするコンテンツによって定義されます。RowScope
と ColumnScope
でのみ使用可能な weight
修飾子を使用すると、コンポーザブルのサイズを親の内部で柔軟に変動するように設定することもできます。
たとえば、2 つの Box
コンポーザブルが含まれる Row
があるとします。最初のボックスには、2 番目のボックスの 2 倍の weight
が指定されているため、幅も 2 倍になります。Row
の幅は 210.dp
なので、最初の Box
の幅は 140.dp
、2 番目のボックスの幅は 70.dp
になります。
@Composable fun ArtistCard(/*...*/) { Row( modifier = Modifier.fillMaxWidth() ) { Image( /*...*/ modifier = Modifier.weight(2f) ) Column( modifier = Modifier.weight(1f) ) { /*...*/ } } }
修飾子の抽出と再利用
複数の修飾子を連鎖させて、コンポーザブルを装飾または拡張できます。このチェーンは、単一の Modifier.Elements
の不変の順序付きリストを表す Modifier
インターフェースを介して作成されます。
各 Modifier.Element
は、レイアウト、描画、グラフィックの動作、ジェスチャー関連のあらゆる動作、フォーカスとセマンティクスの動作、デバイス入力イベントなどの個別の動作を表します。これらの順序は重要です。最初に追加された修飾子要素が最初に適用されます。
同じ修飾子チェーンのインスタンスを複数のコンポーザブルで再利用し、変数に抽出してより高いスコープにホイスティングすると便利な場合があります。コードの読みやすさやアプリのパフォーマンスが改善されることがあります。これには次のような理由があります。
- 修飾子を使用するコンポーザブルに対して再コンポーズが行われると、修飾子の再割り当てが繰り返されない
- 修飾子チェーンは非常に長く複雑になる可能性があるため、同じインスタンスのチェーンを再利用すると、Compose ランタイムで比較時に必要となるワークロードを軽減できる
- この抽出により、コードベース全体で雑多なコードが減り、コードの整合性、保守性が向上します。
修飾子を再利用する際のベスト プラクティス
独自の Modifier
チェーンを作成して抽出し、これらを複数のコンポーザブル コンポーネントで再利用します。修飾子はデータに似たオブジェクトであるため、修飾子を保存することはまったく問題ありません。
val reusableModifier = Modifier .fillMaxWidth() .background(Color.Red) .padding(12.dp)
頻繁に変化する状態を監視しているときの修飾子の抽出と再利用
アニメーションの状態や scrollState
など、コンポーザブル内で頻繁に変化する状態を監視する場合、再コンポーズが頻繁に行われる可能性があります。この場合、修飾子は再コンポーズのたびに割り当てられ、場合によってはフレームごとに割り当てられます。
@Composable fun LoadingWheelAnimation() { val animatedState = animateFloatAsState(/*...*/) LoadingWheel( // Creation and allocation of this modifier will happen on every frame of the animation! modifier = Modifier .padding(12.dp) .background(Color.Gray), animatedState = animatedState ) }
代わりに、次のように同じインスタンスの修飾子を作成、抽出、再利用し、コンポーザブルに渡します。
// Now, the allocation of the modifier happens here: val reusableModifier = Modifier .padding(12.dp) .background(Color.Gray) @Composable fun LoadingWheelAnimation() { val animatedState = animateFloatAsState(/*...*/) LoadingWheel( // No allocation, as we're just reusing the same instance modifier = reusableModifier, animatedState = animatedState ) }
スコープ設定されていない修飾子の抽出と再利用
修飾子は、スコープ設定しないことも、特定のコンポーザブルに対してスコープ設定することもできます。スコープ設定されていない修飾子の場合は、任意のコンポーザブルの外部から単純な変数として修飾子を簡単に抽出できます。
val reusableModifier = Modifier .fillMaxWidth() .background(Color.Red) .padding(12.dp) @Composable fun AuthorField() { HeaderText( // ... modifier = reusableModifier ) SubtitleText( // ... modifier = reusableModifier ) }
これは、Lazy レイアウトと組み合わせると特に便利です。通常の場合には、潜在的に重要な項目には同一の修飾子を使用することをおすすめします。
val reusableItemModifier = Modifier .padding(bottom = 12.dp) .size(216.dp) .clip(CircleShape) @Composable private fun AuthorList(authors: List<Author>) { LazyColumn { items(authors) { AsyncImage( // ... modifier = reusableItemModifier, ) } } }
スコープ設定された修飾子の抽出と再利用
特定のコンポーザブルにスコープ設定する修飾子を処理する場合は、可能な限り高いレベルに修飾子を抽出し、必要に応じて再利用します。
Column(/*...*/) { val reusableItemModifier = Modifier .padding(bottom = 12.dp) // Align Modifier.Element requires a ColumnScope .align(Alignment.CenterHorizontally) .weight(1f) Text1( modifier = reusableItemModifier, // ... ) Text2( modifier = reusableItemModifier // ... ) // ... }
抽出されてスコープ設定された修飾子は、同じスコープの直接の子にのみ渡す必要があります。この制限が重要な理由について詳しくは、Compose でのスコープの安全性のセクションをご覧ください。
Column(modifier = Modifier.fillMaxWidth()) { // Weight modifier is scoped to the Column composable val reusableItemModifier = Modifier.weight(1f) // Weight will be properly assigned here since this Text is a direct child of Column Text1( modifier = reusableItemModifier // ... ) Box { Text2( // Weight won't do anything here since the Text composable is not a direct child of Column modifier = reusableItemModifier // ... ) } }
抽出された修飾子のさらなる連鎖
.then()
関数を呼び出すことで、抽出された修飾子チェーンをさらに連鎖させるか、追加することができます。
val reusableModifier = Modifier .fillMaxWidth() .background(Color.Red) .padding(12.dp) // Append to your reusableModifier reusableModifier.clickable { /*...*/ } // Append your reusableModifier otherModifier.then(reusableModifier)
修飾子の順序が重要であることに留意してください。
詳細
修飾子の全リストと、各修飾子のパラメータとスコープを紹介します。
修飾子の使用方法について詳しくは、Compose の基本レイアウトの Codelab を参照するか、最新の Android リポジトリをご覧ください。
カスタム修飾子とその作成方法について詳しくは、カスタム レイアウト - レイアウト修飾子の使用のドキュメントをご覧ください。
あなたへのおすすめ
- 注: JavaScript がオフになっている場合はリンクテキストが表示されます
- Compose レイアウトの基本
- エディタの操作 {:#editor-actions}
- カスタム レイアウト {:#custom-layouts }