Jetpack Composeでサンプルアプリを作ってみる

# Jetpack Composeとは
AndroidのUIは基本的にはXMLで作成していきますが、`Jetpack Compose`はGoogle I/O 2019で発表された宣言型のUIツールキットです。今は、まだプレアルファ版ですが2020年を目標にベータ版が公開される予定です。

# 作成するサンプルアプリ
今回は、Qiitaの記事を一覧表示するサンプルアプリを作成していきたいと思います。
※サンプルアプリの案は入社当時にお世話になったKotlinスタートブックにあったものを参考にしました)

作成するサンプルアプリの画面は以下のようになっています。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434603/a4c0e289-7d1d-f272-e74e-92080b6654a6.png" width=50% height=50%>

# 環境
Android Studio4.0 Canary6
targetSdkVersion 29

# プロジェクト作成
1. `Start a new Android Studio Project`を押して、`Empty Compose Activity`を選択
2. プロジェク名を入力し、Finish

<img width="100%" height=90% alt="スクリーンショット 2019-12-18 17.09.11.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434603/08fb5607-dbb8-9b5c-5e68-b88c9c3d229d.png">

# Jetpack Composeの使い方
### 基本的な使い方
1. `@Composable`を付けた関数の中に、コンポーネントを記述する
2. setContentの中で`@Composable`を付けた関数を呼び出す

```kotlin:MainActivity.kt
class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            Greeting(name = "Android")
        }
    }
}

@Composable
fun Greeting(name: String) {
   Text (text = "Hello $name!")
}
```

### プレビュー画面で確認
また、Android Studio4.0 Canary1からIDE上でプレビューを見ることができます。
方法としては、確認したいコンポーネントの`@Composable関数`に`@Preview`を付けるだけです。

```kotlin:MainActivity.kt
@Composable
fun Greeting(name: String) {
    Text (text = "Hello $name!")
}

@Preview
@Composable
fun PreviewGreeting() {
    Greeting("Android")
}
```

実際にIDE上に表示されるプレビュー画面はこんな感じです。

<img width=100% height=100% alt="スクリーンショット 2019-12-18 18.09.06.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434603/734e5e45-fd77-5660-c52e-5b9d77a8d95a.png">

# データクラスの作成
では、さっそくサンプルアプリを作成していきます。
まずは、サンプルアプリで使用するデータを定義します。
定義するデータは記事とユーザで、以下のようにクラスを作成しました。

```kotlin:Article.kt
package com.example.qiita.model

data class Article(
    val id: String,
    val title: String,
    val url: String,
    val user: User
)
```

```kotlin:User.kt
package com.example.qiita.model

data class User(
    val id: String,
    val name: String,
    val profileImageUrl: String
)
```

# 記事のコンポーネントを作成

### 下準備
まず、画像を描画するのに`DrawImage`を使う必要があるのですが、プロジェクト作成当初だと使えないので使えるようにしていきます。`build.gradle`に以下のコード追加しました。

```gradle:build.gradle
dependencies{
    //追加するコードだよ
    implementation 'androidx.ui:ui-foundation:0.1.0-dev03'
}
```

### 記事のコンポーネントを作成
`ArticleItem`という`@Composable関数`の中に記事のコンポーネントを作成しました。

```kotlin:MainActivity.kt
@Composable
fun ArticleItem(article: Article) {
    // おそらくまだURLから読み込む方法はない
    val image = +imageResource(R.drawable.ic_header)
    val typography = +MaterialTheme.typography()

    Row(modifier = Spacing(16.dp)) {
        Container(modifier = Size(60.dp, 60.dp)) {
            DrawImage(image = image)
        }
        Column(modifier = ExpandedWidth wraps Spacing(right = 16.dp, left = 16.dp)) {
            Text(article.title, style = typography.h6)
            Text(article.user.name, modifier = Spacing(top = 4.dp), style = typography.subtitle2)
        }
    }
}
```

### それぞれどんな役割？

- Row
    - 複数のコンポーネントを横並びに表示するよ
- Container
    - DrawImageのみだと、元々の画像のサイズを表示してしまうため、指定したサイズのコンテナを作成するよ
- DrawImage
    - 画像を表示するよ
- Column
    - 複数のコンポーネントを縦並びに表示するよ
    - これがないとViewが重なって表示されるよ
- Text
    - 文字列を表示するよ
    - Styleに文字のサイズを指定しているよ
- Spacing
    - marginを付けるよ 

### 実行結果
ダミーデータでArticleItemを実行した結果が以下の様になります。

```kotlin:MainActivity.kt
class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MaterialTheme {
                ArticleItem(
                    article = Article(
                        id = "123",
                        title = "Kotlin入門",
                        url = "http://www.exsample.com/articles/123",
                        user = User(id = "456", name = "たろう", profileImageUrl = "")
                    )
                )
            }
        }
    }
}
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434603/088cfe3f-8704-e8c9-7914-e927fe6f2747.png" width=50% height=50%>

後はこのコンポーネントを複数並べれば完成しそうです。

# 記事一覧のコンポーネントを作成
先程作成した記事のコンポーネントを使って、記事を一覧表示するコンポーネントを作成していきます。

```kotlin:MainActivity.kt
@Composable
fun ArticleList(articles: List<Article>) {
    VerticalScroller {
        Column {
            articles.forEach { article ->
                Card(
                    modifier = Spacing(4.dp) wraps Expanded,
                    shape = RoundedCornerShape(8.dp)
                ) {
                    ArticleItem(article = article)
                }
            }
        }
    }
}
```

### それぞれどんな役割？
- VerticleScroller
    - 縦スクロールできるようになるよ
- Card
    - Androidでいう[CardView](https://developer.android.com/guide/topics/ui/layout/cardview?hl=ja#CardView)だよ

### 実行結果

```kotlin:MainActivity.kt
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val articles = mutableListOf<Article>()
        repeat(11) {
            articles.add(
                Article(
                    id = "123",
                    title = "Kotlin入門",
                    url = "http://www.exsample.com/articles/123",
                    user = User(id = "456", name = "たろう", profileImageUrl = "")
                )
            )
        }

        setContent {
            MaterialTheme {
                ArticleList(articles = articles)
            }
        }
    }
}
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434603/f92ead8c-320e-c9d2-04c6-59f4ca962905.png" width=50% height=50%>

# ツールバーを作成
最後に`setContent`の中に`TopAppBar`を追加してツールバーを作成しました。Columnでコンポーネントを縦並びに配置しています。

```kotlin:MainActivity.kt
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        /**省略**/
       
        setContent {
            MaterialTheme {
                //追加するやつ　　
                Column {
                    //追加するやつ
                    TopAppBar(title = {
                        Text("QiitaClient")
                    })
                    ArticleList(articles = articles)
                }
            }
        }
    }
}
```

# おわりに
Jetpack Composeを使って記事を一覧表示するアプリを作成していきました。
今回のサンプルアプリの記事のデータはダミーデータを使ってるので、今後はQiita APIから記事のデータを取得して表示させたり、ボタンを押すと他の画面に遷移させたりしていきたいなと思います。

使ってみた感想ですが、思っていたより簡単かつシンプルに書けたかなと思っています。ただ、プレビュー画面で作成したコンポーネントを確認する際に`Build Refresh`しなければいけないのでそこが不便だと感じました。今はまだプレアルファ版ですが、ベータ版がすごい楽しみです。

# 参考にしたもの:bow_tone1:
[Kotlinスタートブック](https://books.google.co.jp/books/about/Kotlin%E3%82%B9%E3%82%BF%E3%83%BC%E3%83%88%E3%83%96%E3%83%83%E3%82%AF.html?id=cJD9vQAACAAJ&source=kp_book_description&redir_esc=y)
[Jetpack Compose](https://developer.android.com/jetpack/compose?hl=ja)
[Jetpack Compose Tutorial](https://developer.android.com/jetpack/compose/tutorial)
[android/compose-samples](https://github.com/android/compose-samples/tree/master/JetNews)