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 でより優れたアプリを迅速にビルドする
- Jetpack Compose
- codelab (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 に移行する
例 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も無くなっています。
ActionBar、ToolbarはScaffoldの引数のtopBarに変わっています。
topBarもComposable部品なので、Composable部品を組み合わせて構成することになります。View方式のようなmenu.xmlはありません。
Navigation
View方式もJetpackComposeも画面遷移にNavigationは使えますが、仕組みは全く違います。
JetpackComposeのNavigationはcodelabが参考になります。
ViewModel
ViewModelはView方式と同様使えますが、立ち位置が少々違います。AACにおいて、View方式の場合は画面の一部の情報の状態保持と、Repositoryとの中継(room、HTTP送受信など)にも使われていました。
画面の一部の情報の状態保持はJetpackComposeの場合はremember関数を使います。remember関数を使うことによって、Composeに状態が監視され、状態が変わるとそのCompose部品が再表示されます。
ViewModelはそれ以外の、Repositoryとの中継(room、HTTP送受信など)に使われます。
remember関数も種類がたくさんあります。
remember関数の説明についてはこれが一番、わかりやすかった。
coRoutine
coRoutineもView方式同様、JetpackComposeでも使用されます。
ViewModelの中から使われる場合はscopeは今まで通りviewMoeelScopeでよいですが、Composable部品のAnimetion等にもcoRoutineが使われますので、Composable部品の中からscopeの取得が必要です。
val scope = rememberCoroutineScope()
Room
RoomはView方式同様使えます。RoomはViewModelの中のRepositoryから呼ばれるケースが多いと思うのでそこは変わりません。
View方式でRoomを全件、複数件検索するObserverパターンはViewModelでStateFlowで返す必要があります。
Layout系
UIを定義するLayout.xmlがないので
- ConstraintLayout
- LinerLayout
- FrameLayout
- TableLayout
- 等など・・・
は存在しません。レイアウトはPreviewで確認しながら、Modifierで頑張るしかありません。
この辺はView方式より劣るところです。
UIのテスト
Androidアプリのテストですが、
View方式の場合、テストフレームワークは以下の3つありました。
UI AutometerはE2Eテストなので、JetpackComposeでも変わりません。
公式のcodelabのJetpackComposeのUIテストはInstrumentTestになります。
View方式の場合はRobolectricによるUIテストはlocal Testでできましたが、InstrumentTestになると実行時間が余計にかかります。
以下の資料によるとJetpackComposeでもRobolectricでのLocal Testはできるように書かれていますが、一体どうやってやるのか?サンプルがあれば見てみたいところです。
Junitテストフレームワークは未だにJunit4のようです。(何故、JUnit5に移行しないんだ?)
androidTestImplementation "androidx.compose.ui:ui-test-junit4"
build.gradle.ktsではespressoを依存関係に入れていますが、createComposeRuleがespressoに依存している(内部で呼び出しているのか?)かどうか、よくわかりません。
Droidkaigi 2023ネタ
Droidkaigi 2023のセッションでも似たような話題がありました。
JetpackComposeって、どうなん?まだ、不足しているところ
専用デザイナがない
View方式の場合はAndroidStudioにlayout.xmlを編集できる専用デザイナがあり、その専用デザイナから部品をConstraintLoyoutにポイっとDrag&Drop
して配置を決めれば、それなりに画面デザインできましたが、JetpackComposeにはそのような専用デザイナはありません。UIはすべてコードで書かないといけません。辛うじて、Preview機能はありますがComposable部品単位のPreviewになります。
これはView方式の移行者、新規参入者には厳しいです。上記のようにView方式と同じことを実現する部品自体が変わっていますので、一体、ここで何のComposable部品を使えばいいのか?迷ってしまいます。
ライブラリ
設定画面(View方式の AndroidX Preference)
View方式のAndroidX Preferenceのような、画面を設計する枠組みはまだ標準では用意されていません。
コミニュティのライブラリで、Compose-Settingsがありますが、まだ情報が少くなく、使い方がよくわかりません。
Compose-Settingsの場合は、設定の保存先はSharedPreferencesではなくて、DataStoreを使うのが相性が良さそうです。
(SharedPreferencesでもできるが、googleはSharedPreferencesはobsoluteでDataStoreを推奨している)
このライブラリのui用ライブラリですが、Material2用と3用がありますので注意してください
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^)/
などのメリットがあるので、今後はどんどん普及していくのかなと思います。