ColumnとRowで理解するJetpack Composeのレイアウト基礎

# はじめに

Cloud NextでJetpack Composeは発表され、先日Android Dev Summitでは、大幅なアップデート行われました。

Jetpack Composeのレイアウトを制御するコンポーネントは以下のような階層構造になっています。
下位の階層ほど汎用的なコンポーネントになっています。
上位のコンポーネントは、下位のコンポーネントをラッパーしたものになっており、指定できる機能が限定されています。

![jc1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54186/5d322977-0140-117c-9c2e-1f29d94de47c.png)

これらの階層構造から実際にそれぞれの役割がどのように異なるのかを理解していきたいと思いますが、今回はまずColumnとRowの実際の挙動を確認することで、ColumnとRowの役割を明確にすることでFlexとの違いを理解する第一歩としたいと思います。


## Column基本
縦に内部の要素が並びます。

```kotlin
    Column {
        (1..50).toList().map { it.toString() }.forEach { name ->
            Button(name)
        }
    }
```

![Screenshot_1577164971.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54186/ef9d1858-5313-6533-1b5d-dbbc7806e77b.png)




## ColumnにLayoutModifierを指定

引数modifilerにはLayoutModifier型を指定して、カラムのサイズなどを指定できます。
指定しない場合は、Modifier.None が指定されます。

### modifierにWidthを指定

カラムの幅が40dpになるのでボタンもそれに伴い縮びます。

```kotlin
    Column(modifier = Width(40.dp)){
        (1..50).toList().map { it.toString() }.forEach { name ->
            Button(name)
        }
    }
```

![Screenshot_1577166390.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54186/edf158c4-76da-8eed-d090-086413792608.png)




### modifierにHeightを指定

カラムの高さが400dpになるので、一部のボタンが表示されません。

```kotlin
    Column(modifier = Height(400.dp)){
        (1..50).toList().map { it.toString() }.forEach { name ->
            Button(name)
        }
    }
```
![Screenshot_1577166678.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54186/a297148b-ae52-213f-5739-6b37be5994c9.png)



### WidthとHeightを指定する場合は、Sizeを利用します。

```kotlin
    Column(modifier = Size(width = 400.dp, height = 100.dp)){
        (1..50).toList().map { it.toString() }.forEach { name ->
            Button(name)
        }
    }
```


### modifierにExpandedを指定

利用できるスペースをすべて使うように拡大されます。
わかりやすいように背景のカラーを設定します。

- None

```kotlin
    Surface(color = Color.Gray) {
        Column(modifier = Modifier.None) { // 指定しない場合はNoneになる
            Surface(color = Color.Black) {
                Column(modifier = Width(100.dp)) {
                    (1..50).toList().map { it.toString() }.forEach { name ->
                        Button(name)
                    }
                }
            }
        }
    }
```

![Screenshot_1577693774.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54186/85707ab7-56d0-5726-ee50-31443de08a91.png)


- Expanded

```kotlin
    Surface(color = Color.Gray) {
        Column(modifier = Expanded) {
            Surface(color = Color.Black) {
                Column(modifier = Width(100.dp)) {
                    (1..50).toList().map { it.toString() }.forEach { name ->
                        Button(name)
                    }
                }
            }
        }
    }
```

![Screenshot_1577693870.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54186/e8163c51-3aa1-21d0-0f74-ccfa9256f0fb.png)



他にはExpandedHeight、ExpandedWidthも存在します。

### wrapを用いて複数のmodifierの指定

最初に書いた方が優先されるので、以下の例ではSizeで指定した高さ10px、はHeightで指定した高さ100.dpに上書きされます。


```kotlin
    Column(modifier = Height(100.dp) wraps Size(width = 100.dp , height = 10.dp)){
        (1..50).toList().map { it.toString() }.forEach { name ->
            Button(name)
        }
    }
```

![Screenshot_1577169640.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54186/71a164da-2c34-103a-1d41-af5c30a4c64a.png)


他にもModifierが用意されていそうですが、すべてを一覧で確認するドキュメントが見つからなかったためわかりませんでした。


## ColumnにArrangementを指定

arrangementは配置を意味します。
デフォルトでは `Arrangement.Begin` が指定されています。

以降の例では、Column自体が内部のコンテンツのサイズに縮まってしまうと表示結果に違いがでないため、modifilerにExpandedを指定して説明します。


### arrangementにArrangement.Endを指定

終端に並びます。

```kotlin
    Column(
        modifier = Expanded,
        arrangement = Arrangement.End
    ){
        (1..3).toList().map { it.toString() }.forEach { name ->
            Button(name)
        }
    }
```

![Screenshot_1577167692.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54186/13f5489d-33ff-60c2-2917-5aa2079f4c28.png)


### arrangementにArrangement.Centerを指定

中央に並びます。

```kotlin
    Column(
        modifier = Expanded,
        arrangement = Arrangement.Center
    ){
        (1..3).toList().map { it.toString() }.forEach { name ->
            Button(name)
        }
    }
```

![Screenshot_1577167842.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54186/2a87ba16-9033-998e-f780-15926c1a68b3.png)



### arrangementにArrangement.SpaceAroundを指定

子要素間に等間隔に並べます。開始地点と子要素、子要素と終了地点の間は子要素間のスペースの半分になります。

```kotlin
    Column(
        modifier = Expanded,
        arrangement = Arrangement.SpaceAround
    ){
        (1..3).toList().map { it.toString() }.forEach { name ->
            Button(name)
        }
    }
```

![Screenshot_1577167896.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54186/cb0af992-cf76-8155-f426-51e351b6bdc0.png)


### arrangementにArrangement.SpaceBetweenを指定

子要素間が等間隔に並びます。開始地点、終了地点のスペースはありません。

```kotlin
    Column(
        modifier = Expanded,
        arrangement = Arrangement.SpaceBetween
    ){
        (1..3).toList().map { it.toString() }.forEach { name ->
            Button(name)
        }
    }
```

![Screenshot_1577167953.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54186/1b16e1ef-eae0-1fb6-93d7-26d41950cad1.png)



### arrangementにArrangement.SpaceEvenlyを指定

開始地点、子要素間、終了地点が等間隔に並びます。

```kotlin
    Column(
        modifier = Expanded,
        arrangement = Arrangement.SpaceEvenly
    ){
        (1..3).toList().map { it.toString() }.forEach { name ->
            Button(name)
        }
    }
```

![Screenshot_1577167992.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54186/8ca66bb6-0d9b-d34e-07f3-c4a648cecf8b.png)


## Rowとの違い
RowについてもColumnが縦に子要素を並べるのに対して、横に並べるだけで挙動は同じです。

![Screenshot_1577165219.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/54186/6ab8c862-3f85-59a8-cf00-4882855b6a77.png)


## Flexとの違い

FlexにはColumnに存在しないMainAxisAlignmentとCrossAxisAlignmentが存在します。

MainAxisAlignmentは内部的にはArrangementを持っており、また FlexはFlexLayoutの `arrangement = mainAxisAlignment.arrangement` を渡していることからも同じ役割であることがわかります。

CrossAxisAlignmentは、新規に追加されている値で、おそらくColumnの場合は横方向、Rowの場合は縦方向にレイアウトの制御を行うものだと考えられます。


## まとめ

Columnを例にLayoutModifierとArrangementについて説明しました。
Column, RowとFlexの違いはCrossAxisAlignmentにあることがわかりました。

次回はFlex、FlexColumn、FlexRowを調査します。