Jetpack Compose Beta Overviewの動画を見たのでまとめる

## 注意
- 本記事はYouTubeのAndroid Developersチャンネルにおいて投稿された動画である [Jetpack Compose: Beta overview](https://www.youtube.com/watch?v=Ef1xKWjA9E8)について、動画内で述べられていることと、一部筆者の主観が入っている記事となっております。
- 記事内で使用される画像は、[Jetpack Compose: Beta overview](https://www.youtube.com/watch?v=Ef1xKWjA9E8)より引用させていただいております。(不都合等ありましたらコメントをお願いします)
- 動画を全範囲網羅している記事ではありません。

## Jetpack Composeとは？
![](https://i.imgur.com/RlBwN38.png)
### 1. Modern, declarative UI tool kit
- Composeは、宣言的UIを可能にするUIツールキット。
- 宣言的UIとは？
  - どのように見えるか(What)を記述し、どのように作るか(How)を記述しない

### 2. Built on Kotlin
- Kotlinで作られており、Composeの関数のような、直感的なAPIを構築するのに十分な表現力

### 3. Unbundled
- Composeは他のJetpackライブラリと同様にアンバンドルされています。
- プロジェクトのペースで更新できるように、APIバージョン間で一貫した動作をする
- アンバンドルとは？
  - Androidのプラットフォームに含まれておらず(unbundle)、ライブラリとして追加できる

### 4. Built for interop
- 相互運用(interop)を重視している
- 例えば、新規の画面から徐々にComposeに移行することができるみたいな

## 現在のJetpack Composeの状況
![](https://i.imgur.com/Xm822gY.png)

- Jetpack Composeは現在Betaである(2021/04/15時点)
- APIの洗練と安定化に焦点を当てて開発中

## Jetpack Composeの推しポイント
- ComposeはAndroidのUIをより**はやく・簡単**に実装することができる
- なぜ、はやく・簡単に実装することができるのか？
  - 宣言的とUIツールの2つに分解して説明していく

### まず宣言的について
#### 従来のアプローチ(慣例的なアプローチ)
![](https://i.imgur.com/OYX1Kf9.png)

上の図の例は、入門書などに書かれている一般的な例。

XMLがあり、その状態を変更するために、`findViewById()`などをして、プロパティを設定する。

例えば、DBやネットワークから情報をActivity・Fragmentが受け取ったとき、その情報をもちいてUIを更新するなど。

**この問題点はStateがXMLとFragment/Activityの両方に存在すること。**

例えば、上図のTextViewのテキストをAPIの結果に応じて変化させる場合を考える。

XMLのレイアウトファイルにTextViewがあり、初期のテキストが設定されているとする

そして、APIの結果として、文字列を受け取ったときにその情報を格納する変数をFragment・Activityに定義していたとする。

TextViewの状態、Fragment・Activityの状態の2つが存在する。

Fragment・Activity間の状態を、TextViewの状態に反映するには、`setText()`などを使用しなければいけない。

**この手続き的な操作は、開発者の責任で行う必要があり、バグの原因となってしまう。**


#### JetPack Composeのアプローチ
![](https://i.imgur.com/9uht1DX.png)

上図のようにStateとUIがあり、Stateは変化するもので、UIは普遍で変更されないもの。

**状態が変化した際に、UI全体が再生成**される(効率化のために実際には差分のみの再生成となる)


先ほどの例(TextViewのテキストをAPIの結果に応じて変化させる場合)について考えてみる。

状態はFragment・Activityで保持する１つのみとなり、状態としてTextViewに表示する文字列が入っている。

APIリクエストがあり、状態の変更を検知して、状態をもとにUIを再生成する。

**何が良いのか？**

これにより、状態に応じて自動的にUIが変更されることになる。
例えば、先ほどの例でいうところの、`setText()`という手続きが不要になる。

結果として、手続きの記述漏れなどによるバグがなくなる。


##### コードレベルで確認していく！
![](https://i.imgur.com/2yplAHP.png)

- 上記コードの説明
  - 文字列のリストを表示する単純なコンポーネント
  - 関数は何も返さないが、UIをemitする
  - `message`の中身が空の場合は「No messsages」を、それ以外の場合はメッサージをテキストとして表示
  - 関数が実行されたら、新しいUIが作成される
    - これを**Recomposing**と呼ぶ。メッセージの状態が変わったら、UIが再生成される

###### messageの変化をどう検知するのか？
**`message`をLiveDataとする。**
![](https://i.imgur.com/PBPPhBV.png)

- 上記のコードの説明
  - ViewModelにおいて、`message`をLiveDataで公開

  ```kotlin:ConverationViewmodel.kt
  // おそらくこんな感じ
  class ConversationViewModel: ViewModel() {

    private val _message = MutableLiveData("")
    val message: LiveData<String> = _message
                :
  ```

  - LiveDataで新しいデータを検知するたびに`MessageList（message）`が呼ばれUIが再生成される
  - 注意点
    - Composableはimmutableなので、参照を保持して、後で照会したり、内容を更新することはできない
    - したがって、全ての情報をEmit時にパラメーターとして渡す必要がある

###### しかしながら、コンポーネントが変化できないということを意味するわけではない
すべてのメッセージを選択するチェックボックスを追加して、すべてのメッセージを選択できるようにしてみる。
![](https://i.imgur.com/r1I5ww6.png)

上記のコードだと、チェックボックスをクリックしても視覚的には切り替わらない。(`cheched = false`)としているので。

なので、状態を更新して、チェックボックスをemitする必要がある

**どうするか？**
チェックするかどうかのローカルな状態を持たせる。
![](https://i.imgur.com/Oo6LtLM.png)

そして、`CheckBox()`のパラメータ`onCheckChange`にユーザがチェックしたときのコールバックを設定する。
![](https://i.imgur.com/geHJH5v.png)

このようにすることで、チェックボックスの状態である`selectAll`がクリック時に更新され、チェックボックスへ視覚的な状態が反映される。

**しかしこれだと、UIにロジックがあって気持ち悪い**
下記のように、`MessageList()`のパラメータとして、ステートと更新のためのラムダを渡す。
![](https://i.imgur.com/XXesDVd.png)

#### Composeはどういうアーキテクチャが適しているか？
基本的には、Composeはどのようなアプリケーション・アーキテクチャにも対応できる。


特に一方向性のデータ・フローとの相性が良く、ViewModelが画面の状態のストリームを公開しているとき。
![](https://i.imgur.com/R44BGIO.png)

各コンポーネントはパラメーターとして状態を受け取り、データが更新された場合にのみUIを更新する。

1つのLiveDataを使用することで、状態を一元化でき、画面全体の状態についての理解が簡単になりエラーを減らせる。

#### 宣言的についてのまとめ
Composeを使用すると **Single Source Of Truth(信頼できる唯一の情報源)** の原則が守れる。

慣習的なアプローチでは、Fragment・Activityで保持している状態とViewが保持している状態の2つがあり、バグの原因となる可能性があった。

しかし、Composeを使用すると状態を一元管理することができる。

そして「状態に変化があったときに、自動でUIが変更される」という性質により、バグが生まれにくくなる。




### UIツールについて
ComposeはUIコンポーネントの豊富なツールキットを提供している。

#### 機能やメリット
##### Material Design ComponentとMaterial Themingのサポート
|Material Design Component | Material Theming|
|---|---|
|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199988/8af4c4ed-eea5-9497-737e-7afb54f9fdc8.png)|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199988/972655f6-cd31-d724-9244-98df79d2c201.png)|


##### シンプルだがパワフルなレイアウトシステムを提供
例えば、`Row`というComposableは、LinearLayoutの`orientation`を`horizontal`に指定したレイアウトとほぼ同等。
しかし、Viewシステムが異なり、ネストされたレイアウトをパフォーマンスの高いものにしてくれるメリットがある。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199988/6efcf81f-70ac-386d-68df-3e65d84f944b.png)


より複雑なレイアウトを表現するために、ConstraintLayoutも提供されている。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199988/fdc8ff86-5798-0d5e-51b7-08c226026259.png)


ただし、カスタムレイアウトを使う方が簡単。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199988/3ce33653-b483-4d59-2e55-a931ab60d30b.png)

##### アニメーションシステム
UIに動きをつけることが、より使いやすく、非常にパワフルになっている。
MotionLayoutをComposeに導入する作業も行っている
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199988/a756ac90-fafa-52ca-5358-4e59787411c7.png)

##### テスト
Composeは、テストを容易にするために専用のテストシステムを提供する。
そして、Composablesを個別にテストするための簡単なAPIを提供する。
アニメーションのテストも書くことができる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199988/02da4781-559d-be96-6f15-3df61cf05868.png)


##### Kotlinで書かれている
Kotlinの優れた言語機能のおかげで、シンプルで直感的なAPIとなっている。
例えばCoroutineを使用することによって、シンプルな非同期APIを作成できるようにしている。
Coroutineを使用して、タッチ検出がブロックされないように、新しいコルーチンで押された位置にアニメーションさせている。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199988/85e3cb71-65aa-78d3-e3ef-da33b081965f.png)

#### 具体例で確認していく！
下記レポジトリの中の`Jetchat`というアプリで説明。
https://github.com/android/compose-samples

##### レイアウトの説明
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199988/08611375-bcca-6be3-7754-ed6ed3e21151.png)

上記のレイアウトを作るには下記のことを行っている

- message(=表示するテキスト)、isFromMe(=自分からのメッセージがどうかのフラグ)をパラメータに持つComposableを作成
- メッセージ全体を囲むCard Composableを作成
  - isFromMeに応じて、Themeから色を指定
- 実際にテキストを表示するためにText Composableを作成
  - Modifierパラメータ用いてpaddingを設定

##### 開発・検証方法
`@Preview`アノテーションを用いてプレビュー用のComposableを作成する。
それをAndroid Studio上で確認することが可能
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199988/801c4d6b-e716-f6f3-7df1-80506a0cc62a.png)

この後、細かい実装方法が紹介されているので割愛。
述べられている内容

- ComposableのパラメータにModifierを渡して、再利用可能に
- Modifierでは、最大幅やテキストのAlignmentなどを指定
- `LazyColumn`を使用してRecyclerViewのような効率的なスクロールの実装
- など

興味ある方は、[10:33](https://youtu.be/Ef1xKWjA9E8?t=633)〜[12:59](https://youtu.be/Ef1xKWjA9E8?t=779)

### 推しポイントまとめ
宣言的なUIによって、Composeを使用するとSingle Source Of Truth(信頼できる唯一の情報源)の原則が守れる。
UIツールによって、レイアウトやアニメーションの実装が簡単になる。
-> 簡単に、はやく実装することができる

### まだまだ推しポイントはある
#### 既存のViewシステムと共存できる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199988/f7d4fd6d-8e0c-97dc-ea07-37b6422ea8d1.png)

Interoperability APIと呼ばれるものが用意されている

```xml
:
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">
                      :
    <androidx.compose.ui.platform.ComposeView
        android:id="@+id/compose_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
                      :
</LinearLayout>
```

```kotlin
class ExampleFragment : Fragment() {

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        // Inflate the layout for this fragment
        return inflater.inflate(
            R.layout.fragment_example, container, false
        ).apply {
            findViewById<ComposeView>(R.id.compose_view).setContent {
                // In Compose world
                MaterialTheme {
                    Text("Hello Compose!")
                }
            }
        }
    }
}
```

コードの引用: https://developer.android.google.cn/jetpack/compose/interop?hl=ja#interop-apis

#### 他の主要なライブラリとの統合を提供

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199988/c6417cff-f8f1-49cf-1ef7-b2eb8039ba6c.png)

### 資料等
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199988/94a1173b-c019-e83b-d9f2-b1353ad9cade.png)

Composeの学習系

  - [goo.gle/compose-pathway](https://developer.android.com/courses/pathways/compose)
  - [goo.gle/compose-samples](https://github.com/android/compose-samples)
  - [goo.gle/compose-docs](https://developer.android.com/jetpack/compose/documentation)

フィードバック等

  - [goo.gle/compose-slack](https://surveys.jetbrains.com/s3/kotlin-slack-sign-up)
  - [goo.gle/compose-feedback](https://issuetracker.google.com/issues/new?component=612128)