JetpackComposeって、どうなん？

# JetpackComposeって、どうなん？世の中普及具合
去年の夏ぐらいからAndroidアプリ開発者としてデビュー。1年とちょっとになります。
Androidアプリ開発の世界の色々なことが徐々に見え始めたのと、当時は何もない白紙の状態からのスタートだったのでView方式(Layout.xml、Activity、Fragment)から始めました。

昨今になって、JetpackComposeが正式版となってリリースされ、世の中の話題もJetpackComposeの話題が多くなってきたような気がします。
で、もって、世の中のJetpackComposeの普及具合ですが、大手はView方式からJetpackComposeへの移行を進めているようです。

* メリカリ
* zozo
* Retty
* Wantedly
* ココナラ
* 等など・・・

とは行ってもView方式はAndroid1.0がリリースされた2008年、本格的に使われるようになったAndroid2.3の2010年からの膨大な過去の資産があります。

なので、過去に作ったアプリ、色々なドキュメント、書籍はまだまだView方式が多いというのが実情です。

* Fragmentの導入(Android3.0〜)
* SupportLibraryの廃止、AndroidXへの移行
* 等など・・・

逆に、それだけ長い年数、数々の変遷で機能拡張を続けてきた(APIの新設、廃止)こと考えるとView方式はここらで限界を迎えていると言わざるを得ません。

# JetpackComposeって、どうなん？何が違うねん？
ざっくり言うと
* View方式=**命令的UI**
* JetpackCompose=**宣言的UI**

で概念が全く違います。宣言的UIはここ最近WebフロントではやっているReactでも採用されている概念です。

View方式はAndroidStudioの専用デザイナで画面のUIを設計し、ActivityやFragmentからそのUIの部品に対して表示を**命令**する方式です。
JetpackComposeはComposable部品単位でUIを設計します。Composable部品がComposable部品をネストする感じになります。Composableは状態を監視して(Composable部品の方から状態を取りに行く・・・と言う表現が正しいのか？)状態が変わった場合だけ再表示する仕組みになっています。

Composable部品単位でUIを設計するので、部品の共有はView方式よりもやりやすいのかもしれません。(View方式でもFragmentで共有できないわけでもないが・・・)

なので、View方式でActivity、Fragmentにビジネスロジックを持っていると、そこはかなり変わります。

後述しますが、JetpackComposeにFragmentの概念はありません。(Activityは相変わらずある)Composable部品はみな平等で、階層構造でネストしているので、その中でもトップレベルのComposable部品がFragmentに相当するわけになります。

これも後述しますが、View方式でActivity1個に対して、複数Fragmentで画面遷移するような方式の場合、navigationが使えますが、JetpackComposeもnavigationが使えます。

JetpackComposeは言語はkotlinのみをサポートします。Android開発において、そろそろJava言語とは決別する時期に来ているのかもしれません。

## サンプル、codelab

google公式のcodelabがわかりやすいです。
* [Jetpack Compose でより優れたアプリを迅速にビルドする](https://developer.android.com/jetpack/compose?hl=ja)
* [Jetpack Compose](https://developer.android.com/courses/pathways/compose?hl=ja)
* [codelab](https://codelabs.developers.google.com/?cat=Android&text=compose) (composeで検索)

## Material2とMaterial3がまだ混在している。

View方式もMaterial2からMaterial3(ボタンが少々丸こくなっている)に変わりつつありますが、JetpackComposeもMaterial2とMaterial3が混在しています。

JetpackComposeのUI部品はpackage的には
* androidx.compose.material (Material2)
* androidx.compose.material3 (Material3)

の下に存在しているので、Material2かMaterial3でモノが違います。
Material2とMaterial3でComposable部品の引数が変わっている場合もあるので注意が必要です。

世の中は徐々にMaterial3に移っていくものだと思われます。

[Compose でマテリアル 2 からマテリアル 3 に移行する](https://developer.android.com/jetpack/compose/designsystems/material2-material3?hl=ja#dependencies)

例 Material2：androidx.compose.material.Scaffold
```
implementation "androidx.compose.material:material:$m2-version"
```
```
@Composable
fun Scaffold(
    modifier: Modifier = Modifier,
    scaffoldState: ScaffoldState = rememberScaffoldState(),
    topBar: @Composable () -> Unit = {},
    bottomBar: @Composable () -> Unit = {},
    snackbarHost: @Composable (SnackbarHostState) -> Unit = { SnackbarHost(it) },
    floatingActionButton: @Composable () -> Unit = {},
    floatingActionButtonPosition: FabPosition = FabPosition.End,
    isFloatingActionButtonDocked: Boolean = false,
    drawerContent: (@Composable ColumnScope.() -> Unit)? = null,
    drawerGesturesEnabled: Boolean = true,
    drawerShape: Shape = MaterialTheme.shapes.large,
    drawerElevation: Dp = DrawerDefaults.Elevation,
    drawerBackgroundColor: Color = MaterialTheme.colors.surface,
    drawerContentColor: Color = contentColorFor(drawerBackgroundColor),
    drawerScrimColor: Color = DrawerDefaults.scrimColor,
    backgroundColor: Color = MaterialTheme.colors.background,
    contentColor: Color = contentColorFor(backgroundColor),
    content: @Composable (PaddingValues) -> Unit
): Unit
```

例 Material3：androidx.compose.material3.Scaffold
```
implementation "androidx.compose.material3:material3:$m3-version"
```
```
@Composable
fun Scaffold(
    modifier: Modifier = Modifier,
    topBar: @Composable () -> Unit = {},
    bottomBar: @Composable () -> Unit = {},
    snackbarHost: @Composable () -> Unit = {},
    floatingActionButton: @Composable () -> Unit = {},
    floatingActionButtonPosition: FabPosition = FabPosition.End,
    containerColor: Color = MaterialTheme.colorScheme.background,
    contentColor: Color = contentColorFor(containerColor),
    contentWindowInsets: WindowInsets = ScaffoldDefaults.contentWindowInsets,
    content: @Composable (PaddingValues) -> Unit
): Unit
```

# JetpackComposeって、どうなん？View方式のアレは何に代わった？

## Fragment
Fragmentはなくなっています。View方式の場合はFragmentのクラスとLayout.xmlが1対1でしたが、Layout.xmlも無くなっているので、何がFragmentに相当する？と言われると、＠Composableの階層構造の部品の中でもトップレベルがFragmentに相当するという解釈があっているのかなと思います

## RecyclerView
RecyclerViewも無くなっています。View方式の場合はRecyclerView.Adapterを継承したクラスとViewHolderクラスが必須ですが、そんな概念もなくなっています。
RecyclerViewは
* LazyRow(横方向)
* LazyColumn(縦方向)

に変わっています。

## ActionBar、Toolbar
アプリの上部に表示される[ActionBar、Toolbar](https://developer.android.com/training/appbar?hl=ja)も無くなっています。
ActionBar、ToolbarはScaffoldの引数のtopBarに変わっています。
topBarもComposable部品なので、Composable部品を組み合わせて構成することになります。View方式のようなmenu.xmlはありません。

## Navigation
View方式もJetpackComposeも画面遷移にNavigationは使えますが、仕組みは全く違います。

https://qiita.com/cozyk100/items/1b87072c498aceba8152

JetpackComposeのNavigationはcodelabが参考になります。

https://developer.android.com/codelabs/jetpack-compose-navigation?hl=ja&continue=https%3A%2F%2Fdeveloper.android.com%2Fcourses%2Fpathways%2Fcompose%3Fhl%3Dja%23codelab-https%3A%2F%2Fdeveloper.android.com%2Fcodelabs%2Fjetpack-compose-navigation#0

## ViewModel

ViewModelはView方式と同様使えますが、立ち位置が少々違います。AACにおいて、View方式の場合は画面の一部の情報の状態保持と、Repositoryとの中継(room、HTTP送受信など)にも使われていました。

画面の一部の情報の状態保持はJetpackComposeの場合はremember関数を使います。remember関数を使うことによって、Composeに状態が監視され、状態が変わるとそのCompose部品が再表示されます。

ViewModelはそれ以外の、Repositoryとの中継(room、HTTP送受信など)に使われます。

remember関数も種類がたくさんあります。
remember関数の説明についてはこれが一番、わかりやすかった。

https://engawapg.net/jetpack-compose/2113/remember-tips/

## coRoutine

coRoutineもView方式同様、JetpackComposeでも使用されます。
ViewModelの中から使われる場合はscopeは今まで通りviewMoeelScopeでよいですが、Composable部品のAnimetion等にもcoRoutineが使われますので、Composable部品の中からscopeの取得が必要です。
```kotlin
val scope = rememberCoroutineScope()
```

https://developer.android.com/codelabs/jetpack-compose-animation?hl=ja#0

## Room

RoomはView方式同様使えます。RoomはViewModelの中のRepositoryから呼ばれるケースが多いと思うのでそこは変わりません。
View方式でRoomを全件、複数件検索するObserverパターンはViewModelでStateFlowで返す必要があります。

## Layout系

UIを定義するLayout.xmlがないので
* ConstraintLayout
* LinerLayout
* FrameLayout
* TableLayout
* 等など・・・

は存在しません。レイアウトはPreviewで確認しながら、Modifierで頑張るしかありません。
この辺はView方式より劣るところです。

https://developer.android.com/jetpack/compose/modifiers-list?hl=ja

## UIのテスト

Androidアプリのテストですが、

https://qiita.com/cozyk100/items/550f39d5fbb8df0dbea0

View方式の場合、テストフレームワークは以下の3つありました。
1. [Espresso](https://developer.android.com/studio/test/other-testing-tools/espresso-test-recorder?hl=ja)
1. [Robolectric](https://robolectric.org/)
1. [UI Autometer](https://developer.android.com/training/testing/other-components/ui-automator)

UI AutometerはE2Eテストなので、JetpackComposeでも変わりません。

公式のcodelabのJetpackComposeのUIテストはInstrumentTestになります。
View方式の場合はRobolectricによるUIテストはlocal Testでできましたが、InstrumentTestになると実行時間が余計にかかります。

以下の資料によるとJetpackComposeでもRobolectricでのLocal Testはできるように書かれていますが、一体どうやってやるのか？サンプルがあれば見てみたいところです。

https://speakerdeck.com/mkeeda/compose-deshou-niru-reta-ui-no-unit-test

Junitテストフレームワークは未だにJunit4のようです。(何故、JUnit5に移行しないんだ？)
```
androidTestImplementation "androidx.compose.ui:ui-test-junit4"
```
build.gradle.ktsではespressoを依存関係に入れていますが、createComposeRuleがespressoに依存している(内部で呼び出しているのか？)かどうか、よくわかりません。

https://developer.android.com/codelabs/jetpack-compose-testing?hl=ja#0

## Droidkaigi 2023ネタ
Droidkaigi 2023のセッションでも似たような話題がありました。

https://docs.google.com/presentation/d/1LNn-fI4a3hOFTn93u8hn_9OAon9HnFGkylc1inr6b-o/edit#slide=id.p

# JetpackComposeって、どうなん？まだ、不足しているところ

## 専用デザイナがない
View方式の場合はAndroidStudioにlayout.xmlを編集できる専用デザイナがあり、その専用デザイナから部品をConstraintLoyoutにポイっとDrag&Drop
して配置を決めれば、それなりに画面デザインできましたが、JetpackComposeにはそのような専用デザイナはありません。UIはすべてコードで書かないといけません。辛うじて、Preview機能はありますがComposable部品単位のPreviewになります。

これはView方式の移行者、新規参入者には厳しいです。上記のようにView方式と同じことを実現する部品自体が変わっていますので、一体、ここで何のComposable部品を使えばいいのか？迷ってしまいます。

## ライブラリ
### 設定画面(View方式の AndroidX Preference)

View方式の[AndroidX Preference](https://developer.android.com/guide/topics/ui/settings?hl=ja)のような、画面を設計する枠組みはまだ標準では用意されていません。

コミニュティのライブラリで、[Compose-Settings](https://github.com/alorma/Compose-Settings)がありますが、まだ情報が少くなく、使い方がよくわかりません。
Compose-Settingsの場合は、設定の保存先はSharedPreferencesではなくて、DataStoreを使うのが相性が良さそうです。
(SharedPreferencesでもできるが、googleはSharedPreferencesはobsoluteでDataStoreを推奨している)
このライブラリのui用ライブラリですが、Material2用と3用がありますので注意してください
```kotlin:build.gradle.kts
    implementation("androidx.compose.material3:material3")
    implementation("com.github.alorma:compose-settings-ui-m3:1.0.2") // これ、Material3用
    implementation("com.github.alorma:compose-settings-storage-preferences:1.0.2")
    implementation("com.github.alorma:compose-settings-storage-datastore:0.27.0")
    implementation("com.github.alorma:compose-settings-storage-datastore-proto:0.27.0")
    implementation("androidx.datastore:datastore-preferences:1.0.0")

```

# JetpackComposeって、どうなん？総合的に

JetpackComposeを総合的に見ると、まだまだ不足しているところはありますが、

* UIの柔軟性がView方式よりある。Composable単位で共通化が可能。
* 再ComposeはUIに変更があるところだけ実行されるので、実行が早い。端末の負荷が少ない。
* UIとビジネスロジックの分離がされる。
* UIもビジネスロジックもkotlinコードで統一される。
* 宣言的UIなのでWebフロントで流行りのReact等と思考が近い。
* コード量がView方式より減る(?)。 (これは、View方式のLayout.xmlをステップ数に含めるならそうかもしれない。含めないならJetpackComposeはその分をkotlinコードで書くので逆に増えるのでは？)
* 大手がView方式から移行を進めている。(^O^)／

などのメリットがあるので、今後はどんどん普及していくのかなと思います。