Jetpack composeは、アプリ開発に必要な一通りのUIコンポーネントをライブラリで提供しています。
そのライブラリ中にUI要素を格納する機能をもつコンテナ型のUIがあります。
Column/Row/Boxがコンテナ型のUIです。構成や使用方法などをまとめます。
※環境:Android Studio Hedgehog | 2023.1.1 Patch 1
Kotlin 1.8.10
Compose Compiler 1.4.3
目次
コンテナ型のUI
自身の中に子UI要素を格納(配置)する機能をもつUI要素(Composable関数)があります。ここでは、それを「コンテナ型のUI」と呼ぶことにします。
次にあげるUI要素はコンテナ型のUIです。
関数 | 配置 |
---|---|
Column | 子UI要素を垂直に並べる |
Row | 子UI要素を水平に並べる |
Box | 子UI要素を子UI要素の上に配置 |
これ以外に、同等なUI要素としてLazyColumn/Row(リスト)・Lazy***Grid(グリッド)などもありますが、用法が異なるので、本記事では取り挙げません。
しかし、子UI要素のサイズと位置を考慮した、配置制御を行いません。コンテナというよりも、UI階層を伴った単なるグループ化です。
private val Red = Color(255, 0, 0, 30) private val Green = Color(0, 255, 0, 30) private val Blue = Color(0, 0, 255, 30) private val Gray = Color(230, 230, 230, 255) @Composable fun ContainerFunc() { Func { Text( text = "あいうえおん", fontSize = 20.sp, modifier = Modifier.background(color = Red) ) Text( text = "ABCD", fontSize = 20.sp, modifier = Modifier.background(color = Blue) ) Text( text = "#$", fontSize = 20.sp, modifier = Modifier.background(color = Green) ) } } @Composable fun Func(content: @Composable () -> Unit) { content() }
Column
ColumnはUI要素を垂直に並ならべるコンテナ型のUIです。子UI要素のサイズと位置を取得して配置します。
関数の引数
Columnの状態(コンテンツ、装飾、演出など)は、引数により指定できます。
@Composable inline fun Column( modifier: Modifier = Modifier, verticalArrangement: Arrangement.Vertical = Arrangement.Top, horizontalAlignment: Alignment.Horizontal = Alignment.Start, content: @Composable ColumnScope.() -> Unit ) { ... }
引数 | 概要 | |
---|---|---|
modifier | Modifier | UI全般のCompose修飾子 |
verticalArrangement | Arrangement.Vertival | コンテンツの配置(垂直方向) デフォルト:Top |
horizontalAlignment | Alignment.Horizontal | コンテンツの配置(水平方向) デフォルト:Start |
contet | ColumnScope.() -> Unit | 格納するコンテンツ |
配置パラメータ | 概要 | |
---|---|---|
Alignment | CenterHorizontally | 水平方法の中央 |
Start | 左端 | |
End | 右端 | |
Arrangement | Center | 垂直方向の中央 |
Top | 上端 | |
Bottom | 下端 | |
SpaceAround | 空きスペースの均一化(後述) | |
SpaceBetween | ||
SpaceEvenly |
Columnの例
コンテンツ(子UI要素、Textが3つ)を中央に配置する例です。
@Composable fun ContainerColumn() { Column( modifier =Modifier.background(color = Gray), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center ) { Text( text = "あいうえおん", fontSize = 20.sp, modifier = Modifier.background(color = Red) ) Text( text = "ABCD", fontSize = 20.sp, modifier = Modifier.background(color = Blue) ) Text( text = "#$", fontSize = 20.sp, modifier = Modifier.background(color = Green) ) } }
Row
RowはUI要素を水平に並ならべるコンテナ型のUIです。子UI要素のサイズと位置を取得して配置します。
関数の引数
Rowの状態(コンテンツ、装飾、演出など)は、引数により指定できます。
@Composable inline fun Row( modifier: Modifier = Modifier, horizontalArrangement: Arrangement.Horizontal = Arrangement.Start, verticalAlignment: Alignment.Vertical = Alignment.Top, content: @Composable RowScope.() -> Unit ) { ... }
引数 | 概要 | |
---|---|---|
modifier | Modifier | UI全般のCompose修飾子 |
horizontalArrangement | Arrangement.Horizontal | コンテンツの配置(水平方向) デフォルト:Start |
verticalAlignment | Alignment.Vertical | コンテンツの配置(垂直方向) デフォルト:Top |
content | @Composable RowScope.() -> Unit | 格納するコンテンツ |
配置パラメータ | 概要 | |
---|---|---|
Alignment | CenterVertical | 垂直方向の中央 |
Top | 上端 | |
Bottom | 下端 | |
Arrangement | Center | 水平方向の中央 |
Start | 左端 | |
End | 右端 | |
SpaceAround | 空きスペースの均一化(後述) | |
SpaceBetween | ||
SpaceEvenly |
Rowの例
コンテンツ(子UI要素、Textが3つ)を中央に配置する例です。
@Composable fun ContainerRow() { Row( modifier =Modifier.background(color = Gray), horizontalArrangement = Arrangement.Center, verticalAlignment = Alignment.CenterVertically ) { Text( text = "あいうえおん", fontSize = 20.sp, modifier = Modifier.background(color = Red) ) Text( text = "ABCD", fontSize = 20.sp, modifier = Modifier.background(color = Blue) ) Text( text = "#$", fontSize = 20.sp, modifier = Modifier.background(color = Green) ) } }
Box
Boxは子UI要素を子UI要素の上に配置するコンテナ型のUIです。子UI要素のサイズと位置を取得して配置します。
関数の引数
Boxの状態(コンテンツ、装飾、演出など)は、引数により指定できます。
@Composable inline fun Box( modifier: Modifier = Modifier, contentAlignment: Alignment = Alignment.TopStart, propagateMinConstraints: Boolean = false, content: @Composable BoxScope.() -> Unit ) { ... }
引数 | 概要 | |
---|---|---|
modifier | Modifier | UI全般のCompose修飾子 |
contentAlignment | Alignment | コンテンツの配置 デフォルト:TopStart |
propagateMinConstraints | Boolean | |
content | @Composable BoxScope.() -> Unit | 格納するコンテンツ |
配置パラメータ | 概要 | |
---|---|---|
Alignment | TopStart | (y、x)←(上端、左端) |
TopCenter | (y、x)←(上端、水平方向の中央) | |
TopEnd | (y、x)←(上端、右端) | |
CenterStart | (y、x)←(垂直方向の中央、左端) | |
Center | (y、x)←(垂直方向の中央、水平方向の中央) | |
CenterEnd | (y、x)←(垂直方向の中央、右端) | |
BottomStart | (y、x)←(下端、左端) | |
BottomCenter | (y、x)←(下端、水平方向の中央) | |
BottomEnd | (y、x)←(下端、右端) |
Boxの例
コンテンツ(子UI要素、Textが3つ)を中央に配置する例です。
@Composable fun ContainerBox() { Box( modifier =Modifier.background(color = Gray), contentAlignment = Alignment.Center ) { Text( text = "あいうえおん", fontSize = 20.sp, modifier = Modifier.background(color = Red) ) Text( text = "ABCD", fontSize = 20.sp, modifier = Modifier.background(color = Blue) ) Text( text = "#$", fontSize = 20.sp, modifier = Modifier.background(color = Green) ) } }
関数の構成
コンテナ型のUIはLayout関数(Composable関数)のみで構成されます。
Layout関数は描画処理のLayoutフェーズで呼び出されて(Layout関数は定義のみ、実際の呼び出し先はmesurePolicyの関数)、UI要素のサイズや位置を算出します。
ドキュメントはLayout関数を「レイアウトノード」と呼んでいます。
※描画処理については「Jetpack Compose:Composeによるアプリ画面の描画」を参照
※レイアウトノードについては「制約と修飾子の順序」を参照
つまり、コンテナ型のUIは、子UI要素の配置しか行いません。
@Composable inline fun Column( modifier: Modifier = Modifier, verticalArrangement: Arrangement.Vertical = Arrangement.Top, horizontalAlignment: Alignment.Horizontal = Alignment.Start, content: @Composable ColumnScope.() -> Unit ) { val measurePolicy = columnMeasurePolicy( verticalArrangement, horizontalAlignment ) Layout( content = { ColumnScopeInstance.content() }, measurePolicy = measurePolicy, modifier = modifier ) }
@Composable inline fun Row( modifier: Modifier = Modifier, horizontalArrangement: Arrangement.Horizontal = Arrangement.Start, verticalAlignment: Alignment.Vertical = Alignment.Top, content: @Composable RowScope.() -> Unit ) { val measurePolicy = rowMeasurePolicy( horizontalArrangement, verticalAlignment ) Layout( content = { RowScopeInstance.content() }, measurePolicy = measurePolicy, modifier = modifier ) }
@Composable inline fun Box( modifier: Modifier = Modifier, contentAlignment: Alignment = Alignment.TopStart, propagateMinConstraints: Boolean = false, content: @Composable BoxScope.() -> Unit ) { val measurePolicy = rememberBoxMeasurePolicy( contentAlignment, propagateMinConstraints ) Layout( content = { BoxScopeInstance.content() }, measurePolicy = measurePolicy, modifier = modifier ) }
@Suppress("ComposableLambdaParameterPosition") @UiComposable @Composable inline fun Layout( content: @Composable @UiComposable () -> Unit, modifier: Modifier = Modifier, measurePolicy: MeasurePolicy ) { val compositeKeyHash = currentCompositeKeyHash val localMap = currentComposer.currentCompositionLocalMap ReusableComposeNode<ComposeUiNode, Applier<Any>>( factory = ComposeUiNode.Constructor, update = { set(measurePolicy, SetMeasurePolicy) set(localMap, SetResolvedCompositionLocals) @OptIn(ExperimentalComposeUiApi::class) set(compositeKeyHash, SetCompositeKeyHash) }, skippableUpdate = materializerOf(modifier), content = content ) }
配置の方法(縦・横に並べる、重ねる)がLayout関数に与えられる引数measurePolicyで決められています。Column/Row/Boxの違いは、このmeasurePolicyの違いです。
weightによるサイズ指定
weightは子UI要素のサイズに重み付けをするCompose修飾子です。
コンテナ型のUIのサイズ(縦または横のサイズ)へピッタリと収まるように、重みに合った比率で子UI要素のサイズを分配します。
以下はRowのサンプルです。並びが縦になるだけで、Columnも同様です。
weightあり
重みに合った比率でサイズを分配します。
@Preview_mdpi @Composable fun ImagesList1() { Row(modifier = Modifier.fillMaxWidth()) { Image( painterResource(R.drawable.azami60x60), // イメージサイズ:60x60[dp] contentDescription = null, contentScale = ContentScale.FillWidth, modifier = Modifier.weight(3.0f) ) Image( painterResource(R.drawable.azami60x60), contentDescription = null, contentScale = ContentScale.FillWidth, modifier = Modifier.weight(1.0f) ) Image( painterResource(R.drawable.azami60x60), contentDescription = null, contentScale = ContentScale.FillWidth, modifier = Modifier.weight(2.0f) ) } }
weightなし
コンテンツのサイズ(Imageのサイズ)で表示するように努めます。
コンテナサイズ ≧ コンテンツ合計サイズデフォルトは左詰めで配置され、右側は空きます。
@Preview_mdpi @Composable fun ImagesList2_1() { Row(modifier = Modifier.fillMaxWidth()) { for(i in 0..2) { Image( painterResource(R.drawable.azami60x60), // イメージサイズ:60x60[dp] contentDescription = null, contentScale = ContentScale.FillWidth, modifier = Modifier ) } } }
コンテンツのサイズ(Imageのサイズ)で表示するように努めますが、端数(5番目のImage)が出た場合は、それがそのままコンテンツのサイズになります。
溢れてしまったコンテンツ(6番目のImage)は表示されません。
@Preview_mdpi @Composable fun ImagesList2_2() { Row(modifier = Modifier.fillMaxWidth()) { for(i in 0..6) { Image( painterResource(R.drawable.azami60x60), // イメージサイズ:60x60[dp] contentScale = ContentScale.FillWidth, contentDescription = null, modifier = Modifier ) } } }
weightあり・なし混合
先にweightなしのコンテンツのサイズを確保した後、残りを重みに合った比率で分配します。
@Preview_mdpi @Composable fun ImagesList3() { Row(modifier = Modifier.fillMaxWidth()) { Image( painterResource(R.drawable.azami60x60), // イメージサイズ:60x60[dp] contentDescription = null, contentScale = ContentScale.FillWidth, modifier = Modifier.weight(1.0f) ) Image( painterResource(R.drawable.azami60x60), contentDescription = null, contentScale = ContentScale.FillWidth, modifier = Modifier.weight(2.0f) ) Image( painterResource(R.drawable.azami60x60), contentDescription = null, contentScale = ContentScale.FillWidth, modifier = Modifier ) } }
特殊な配置(空きスペースの均一化)
子UI要素を配置した時に生まれる空きスペースを均一化することが出来ます。
例はColumnの場合ですが、均一化の向きが異なるだけで、Rowも考え方は同じです。
@Composable fun ContainerColumn() { Column( modifier =Modifier.background(color = Gray), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.SpaceAround // verticalArrangement = Arrangement.SpaceBetween // verticalArrangement = Arrangement.SpaceEvenly ) { Text( text = "あいうえおん", fontSize = 20.sp, modifier = Modifier.background(color = Red) ) Text( text = "ABCD", fontSize = 20.sp, modifier = Modifier.background(color = Blue) ) Text( text = "#$", fontSize = 20.sp, modifier = Modifier.background(color = Green) ) } }
Arrangement.SpaceAround
Arrangement.SpaceBetween
Arrangement.SpaceEvenly
関連記事: