Compose UI:Column/Row/Box(コンテナ型のUI)

投稿日:  更新日:

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要素を垂直に並べる
Columnにおける要素の配置
Row子UI要素を水平に並べる
Rowにおける要素の配置
Box子UI要素を子UI要素の上に配置
Boxにおける要素の配置

これ以外に、同等なUI要素としてLazyColumn/Row(リスト)・Lazy***Grid(グリッド)などもありますが、用法が異なるので、本記事では取り挙げません。

ノーマル関数
ノーマル関数は左上を基準に子UI要素を上に重ねて配置するので、コンテナ型のUIと似ています。

ノーマル関数における要素の配置

しかし、子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
) { ... }
引数概要
modifierModifierUI全般のCompose修飾子
verticalArrangementArrangement.Vertivalコンテンツの配置(垂直方向)
デフォルト:Top
horizontalAlignmentAlignment.Horizontalコンテンツの配置(水平方向)
デフォルト:Start
contetColumnScope.() -> Unit格納するコンテンツ
配置パラメータ概要
AlignmentCenterHorizontally水平方法の中央
Start左端
End右端
ArrangementCenter垂直方向の中央
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)
        )
    }
}

Columnの例

スポンサーリンク

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
) { ... }
引数概要
modifierModifierUI全般のCompose修飾子
horizontalArrangementArrangement.Horizontalコンテンツの配置(水平方向)
デフォルト:Start
verticalAlignmentAlignment.Verticalコンテンツの配置(垂直方向)
デフォルト:Top
content@Composable RowScope.() -> Unit格納するコンテンツ
配置パラメータ概要
AlignmentCenterVertical垂直方向の中央
Top上端
Bottom下端
ArrangementCenter水平方向の中央
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)
        )
    }
}

Rowの例

スポンサーリンク

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
) { ... }
引数概要
modifierModifierUI全般のCompose修飾子
contentAlignmentAlignmentコンテンツの配置
デフォルト:TopStart
propagateMinConstraintsBoolean
content@Composable BoxScope.() -> Unit格納するコンテンツ
配置パラメータ概要
AlignmentTopStart(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)
        )
    }
}

Boxの例

スポンサーリンク

関数の構成

コンテナ型のUIはLayout関数(Composable関数)のみで構成されます。

Layout関数は描画処理のLayoutフェーズで呼び出されて(Layout関数は定義のみ、実際の呼び出し先はmesurePolicyの関数)、UI要素のサイズや位置を算出します。

ドキュメントはLayout関数を「レイアウトノード」と呼んでいます。

※描画処理については「Jetpack Compose:Composeによるアプリ画面の描画」を参照
※レイアウトノードについては「制約と修飾子の順序」を参照

つまり、コンテナ型のUIは、子UI要素の配置しか行いません。

ColumnRowBox
@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あり

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
            )
        }
    }
}

weightなし

 コンテナサイズ < コンテンツ合計サイズ 

コンテンツのサイズ(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あり・なし混合

先に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
        )
    }
}

weightあり・なしの混合

スポンサーリンク

特殊な配置(空きスペースの均一化)

子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

SpaceArroundの例

Arrangement.SpaceBetween

SpaceBetweenの例

Arrangement.SpaceEvenly

SpaceEvnlyの例

スポンサーリンク

関連記事:

Jetpack composeは、アプリ開発に必要な一通りのUIコンポーネントをライブラリで提供しています。 そのライブラリ中のButtonについて、構成や使用方法などをまとめます。 ※環境:Android Studio Flamingo | 2022.2.1    :androidx.compose.material3:material3:1.1.1    :androidx.compose.ui:ui:1.4.3 ...
Jetpack composeは、アプリ開発に必要な一通りのUIコンポーネントをライブラリで提供しています。 そのライブラリ中のSurfaceについて、構成や使用方法などをまとめます。 ※環境:Android Studio Flamingo | 2022.2.1    :androidx.compose.material3:material3:1.1.1    :androidx.compose.ui:ui:1.4.3 ...
Jetpack composeは、アプリ開発に必要な一通りのUIコンポーネントをライブラリで提供しています。 そのライブラリ中のRadioButtonについて、構成や使用方法などをまとめます。 ※環境:Android Studio Hedgehog | 2023.1.1     Kotlin 1.8.10     Compose Compiler 1.4.3 ...
Jetpack composeは、アプリ開発に必要な一通りのUIコンポーネントをライブラリで提供しています。 そのライブラリ中のImageについて、構成や使用方法などをまとめます。 ※環境:Android Studio Hedgehog | 2023.1.1 Patch 2     Kotlin 1.9.0     Compose Compiler 1.5.1     androidx.compose.foundation 1.5.0 ...
Jetpack composeは、アプリ開発に必要な一通りのUIコンポーネントをライブラリで提供しています。 そのライブラリ中のDividerについて、構成や使用方法などをまとめます。 ※環境:Android Studio Hedgehog | 2023.1.1 Patch 2     Kotlin 1.9.0     Compose Compiler 1.5.1     androidx.compose.material3:material3 1.1.1 ...
Jetpack composeは、アプリ開発に必要な一通りのUIコンポーネントをライブラリで提供しています。 そのライブラリ中のSpacerについて、構成や使用方法などをまとめます。 ※環境:Android Studio Hedgehog | 2023.1.1 Patch 2     Kotlin 1.9.0     Compose Compiler 1.5.1     androidx.compose.foundation 1.5.0 ...
Jetpack composeは、アプリ開発に必要な一通りのUIコンポーネントをライブラリで提供しています。 そのライブラリ中のSliderについて、構成や使用方法などをまとめます。 ※環境:Android Studio Hedgehog | 2023.1.1 Patch 2     Kotlin 1.9.0     Compose Compiler 1.5.1     androidx.compose.material3:material3 1.1.1 ...
スポンサーリンク