Jetpack ComposeでXMLレイアウトからの卒業（超初級）

## はじめに
PHONE APPLI Advent Calender13日目です！
Jetpack ComposeやらKotlin CoroutineやらDagger Hiltやら色々勉強つまみ食いしすぎて、逆に分けわからなくなっているしがないAndroidエンジニアです。
今回は最近（？）話題のコードベースでレイアウトを構築できるJetpack Composeについて、自分なりに理解したことを初歩の初歩部分ですが記事にしていきたいと思います。

## Jetpack Composeってそもそも何？
Jetpack Compose は、ネイティブ UI をビルドする際に推奨される Android の最新ツールキットです。Android での UI 開発を簡素化し、加速します。少ないコード、パワフルなツール、直感的な Kotlin API を使用してアプリをすぐに動かすことができます。
(※Android Developersサイトから抜粋)

うーん、なんかよく分からん。
百聞は一見にしかずということで、早速使ってみましょう！

## まずはJetpack Composeプロジェクトの作成
Android Studio(画像はver: Android Studio Giraffe | 2022.3.1 Patch 1)ではJetpack Composeを利用するプロジェクトをテンプレートから作成出来ます。
Android Studioのメニューから `[File] -> [New] -> [New Project...]` と順に選んでいき、下記画像で選択状態になっている　`Empty Activity` でJetpack Composeプロジェクトを作成出来ます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588142/cdf2e87a-c52e-12df-0e85-7fce379b0603.png)

作成されたプロジェクトの内部を見てみると、今まではリソースファイルにレイアウト用のXMLファイルが存在していましたが、Jetpack Composeのプロジェクトでは存在しません。
じゃあどうやって画面描画すんねん！と思ってしまいますが、次の章で見ていきましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588142/2e26f71c-f003-fe47-3316-d3dc6fd5ff12.png)

## テンプレートで作成されたMainActivityの中身
色々書いてありますが、 `setContent` 内に記述される内容が従来の `setContentView` でXMLレイアウトを指定しているものと同等になります。
`setContent` 内にはComposable関数と呼ばれるUIを構成する要素を定義した関数を記述できます。
このComposable関数というものを利用して、UIを構築していきます。

```Kotlin:MainActivity
setContent {
    MyApplicationTheme {
        // A surface container using the 'background' color from the theme
        Surface(
            modifier = Modifier.fillMaxSize(),
            color = MaterialTheme.colorScheme.background
        ) {
            Greeting("Android")
        }
    }
}
```

## UI部品の一例
一部例や使い方を示していこうと思います。カッコ内は従来のXMLレイアウトで使用していたものを記載しています。
- Text（TextView）
テキストの表示を行えます。
下記では引数に文字列しか指定していませんが、 `fontSize` で文字サイズの指定や `color` で色の指定等、引数を与えることによってテキストのプロパティを変更できます。
また、 `stringResource` を利用することによって、リソースから文字列を取得することも出来ます。
  ```Kotlin
  Text(text = "テキスト")
  Text(text = stringResource(id = /* stringリソース */)
  ```
  <br>

- Image（ImageView）
画像の表示に利用するComposable関数で、 `painterResource` で `drawable` リソースから画像を取得、表示できます。
  ```Kotlin
  Image(
    painter = painterResource(id = /* drawableリソース */),
    contentDescription = "/* 画像の内容 */"
  )
  ```
  ここで、リソースからの画像を表示するのはいいけど、URLから画像を取得して読み込むのもこれでできるの？という疑問が浮かんでくると思いますが、標準で用意されているものでは不可能みたいです。
  お馴染みのライブラリであるGlideを利用することや、**Coil**と呼ばれるサードパーティライブラリを利用すると良いと [developer guide](https://developer.android.com/jetpack/compose/graphics/images/loading?hl=ja) に記載されています。
  今回、URLからの画像読み込みについては割愛しますがアプリを作成するには必須となることが多いので、追々学んでいきたいですね。
  <br>
- Row（LinearLayoutでのhorizontal指定）やColumn（LinearLayoutでのvertical指定）
View群を並べる役割の親要素（ViewGroup）です。
それぞれブロックの中に前述した `Text` や、 `Image` 等Composable関数を記述することによってUIを自由に構築することができます。
今回はLinearLayoutのような単純な縦並び・横並びを紹介していますが、ConstraintLayoutのようなViewに他Viewとの制約を持たせることができるもの等、他にも色々なものが用意されています。
  ```Kotlin
  Row {
    Text(text = "テスト１")
    Text(text = "テスト2")
  }

  Column {
    Text(text = "テスト１")
    Text(text = "テスト2") 
  }
  ```
  | Row | Column |
  |:-:|:-:|
  | <img width="250" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588142/fa6b9eec-f460-ffc9-3f78-0bf366dd3637.png"> | <img width="250" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588142/1c859155-a1f3-e364-217f-bdf61fcc8cf0.png"> |


### View共通要素である余白（padding, margin）とかサイズ指定は？
従来でいう `layout_width` や `margin`, `background` の指定についてですが、各標準のComposable関数に引数として **Modifier** というものが用意されています。
このModifierオブジェクトを変更することによって高さや幅、バックグラウンドや余白等の指定を行うことができます。

```Kotlin
Row(
    modifier = Modifier
        .background(Color.Green)
        .height(300.dp)
        .fillMaxWidth()
        .padding(16.dp)
        .background(Color.Black)
        .padding(16.dp)
        .background(Color.Blue),
    verticalAlignment = Alignment.CenterVertically,
    horizontalArrangement = Arrangement.Center
) {
    Text(
        modifier = Modifier
            .padding(horizontal = 16.dp)
            .fillMaxWidth()
            .background(Color.White),
        text = "テスト",
        color = Color.Black,
        textAlign = TextAlign.Center
    )
}
```

`Modifier` では `padding` と `margin` で区別されおらず、要素の指定順序によって表現されます。

```Kotlin:上記抜粋
Modifier
    .background(Color.Green)
    .height(300.dp)
    .fillMaxWidth()
    .padding(16.dp)
    .background(Color.Black)
```
全体の色をグリーンで塗りつぶした上で縦幅を `300dp` ,横幅を親要素いっぱいに表示してから更に `16dp` の余白をとり、その中身を黒く塗りつぶすような記載順序になっています。
`padding` で指定した分、その後に指定する要素が狭まっていくといった形になります。

<img width="250" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588142/d990c2f6-bcb3-49ed-3653-554cce1f5f60.png">

### Composable関数にmodifier引数を定義し、柔軟性を持たせる
前述したように、Modifierオブジェクトを利用するとViewの余白やバックグラウンドを変更できますが、全ての場面で同一の余白やバックグラウンドを利用する場面は少ないと思います。
そこで以下のように自分で定義したComposable関数の引数にModifierを渡すことによって柔軟性を持たせることもできます。
上で作成した `Row` ブロックを別関数で定義して　 `setContent` 内で利用してみます。

```Kotlin
setContent {
    FlexModifierTest(Modifier.padding(20.dp))
}

@Composable
fun FlexModifierTest(modifier: Modifier = Modifier) {
    Row(
        modifier = modifier
            .background(Color.Green)
            .height(300.dp)
            .fillMaxWidth()
    ...
}
```
<img width="250" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588142/64bd0f31-dbde-37bf-b1b1-0d4e6e0e574a.png">

このように関数にComposableアノテーションを使用することで、Composable関数を作成することが可能です。
Composable関数を作成した上で、利用する箇所によって値を変更出来るようにしておくと、UI部品として様々な箇所で流用することができ、アプリ全体である程度レイアウトの統一を行うことが可能です。

## 構築したUIはビルドするまで分からない？
XMLレイアウトだと作成したレイアウトを確認する用のウィンドウがAndroid Studio側で用意されており、見た目を確認しながらレイアウトを構築することが可能でしたが、Jetpack Composeを利用したレイアウトでも同様のことが可能です。

その方法は引数なしのComposable関数にPreviewアノテーションを付けるだけです。
Previewアノテーションを付けることにより、Android Studioのコードウィンドウの右上に存在する `Code` `Split` `Design` 3つのタブの内 `Split` と `Design` のどちらかを選択することによってPreviewアノテーションを付けたComposable関数のレイアウトを確認出来ます。
```Kotlin
@Preview
@Composable
fun FlexModifierTestPreview() {
    FlexModifierTest(Modifier.padding(20.dp))
}
```
引数が存在するとレイアウトの内容が確定しない可能性を孕むので、Previewアノテーションを付けることが出来ません。
Previewアノテーションは同じファイル内の関数であってもいくつでも付けることができるので、引数を持つComposable関数の値を変更したものを複数用意することによってレイアウトの比較等行えます。

## おわりに
今回紹介したものは、ほんのさわりですが利用してみると従来のXMLレイアウトを確認して、コードを書き換えて...みたいな作業が発生しなくなるので、それだけで開発スピードがあがるのではと思いました。
また、UIを部品として定義することによって再利用性が高いという点や全てコードで完結することによってXMLレイアウトとのずれを意識しなくて良い点、標準で用意されているComposable関数がマテリアルデザインに沿った作りになっているので、デザインチームとの齟齬が起きづらくなるという点も有用性が高いと感じます。

まだまだスタート地点に立ったばかりなので、今後もJetpack Composeを利用する場合のアーキテクチャやデータの流れを考慮してのEvent処理や画面の更新等行うには、どう実装していくのがベターなのかを模索するためにも、学習を続けていきたいと思います。

ほんのさわりを記事にするだけでも、とても長くなってしまい読み苦しいところも多々あったと思いますが、ご一読頂きありがとうございます。

composeでUI構築して、coroutineで非同期通信して、dagger hiltで依存性を排除してテストを作りやすくして...夢が広がりんぐですね。
（頓挫しないよう勉強しよう...）