[DroidKaigi 2024] Android ViewからJetpack Composeへ 〜Jetpack Compose移行のすゝめ〜 / From Android View to Jetpack Compose: A Guide to Migration
by
syarihu
Link
Embed
Share
Beginning
This slide
Copy link URL
Copy link URL
Copy iframe embed code
Copy iframe embed code
Copy javascript embed code
Copy javascript embed code
Share
Tweet
Share
Tweet
Slide 1
Slide 1 text
Android Viewから Jetpack Composeへ 〜Jetpack Compose移行のすゝめ〜 Taichi Sato / @syarihu Giftmall, Inc. / Mikan, Inc. Android Engineer 1
Slide 2
Slide 2 text
Agenda 2 Jetpack ComposeとAndroid View Composeの簡単な説明とAndroid Viewとの違いについて Jetpack Composeの導入とルール決め 命名規則や状態の持ち方などのルール決め、画面までプレビューす るための設計など 実例を元にしたJetpack Composeへの移行 オンボーディング画面を例にしてレイアウトの確認から実際の動作 に近いプレビューの作成まで具体的に解説 01 02 03
Slide 3
Slide 3 text
Jetpack ComposeとAndroid View 3
Slide 4
Slide 4 text
Android Viewはメンテナンスモード ? ● 公式なドキュメントなどでは特に明言はされていないが、ところ どころにある情報を見るとメンテナンスモードっぽいコメントが ある 4
Slide 5
Slide 5 text
GridLayoutはメンテナンスモード https://issuetracker.google.com/issues/139991767#comment13 5
Slide 6
Slide 6 text
DataBindingもメンテナンスモード 6 https://issuetracker.google.com/issues/173030256#comment10
Slide 7
Slide 7 text
Android Viewはメンテナンスモード Jetpack Composeの生みの親の投稿 7
Slide 8
Slide 8 text
Jetpack Composeの紹介ページ https://developer.android.com/compose?hl=en 8
Slide 9
Slide 9 text
Android Viewはメンテナンスモード ? ● Jetpack Composeの紹介ページにもJetpack Composeは ネイティブUIを構築するためのAndroid推奨のモダンツール キットであることが記載してある ● Android Viewはもうメンテナンスモードであると考えて良さそ う 9
Slide 10
Slide 10 text
単純なリストを例にした ViewとComposeの違い 10
Slide 11
Slide 11 text
リストを例にしたViewとComposeの違い / Viewの場合 11
Slide 12
Slide 12 text
リストを例にしたViewとComposeの違い / Viewの場合 12
Slide 13
Slide 13 text
リストを例にしたViewとComposeの違い / Viewの場合 13
Slide 14
Slide 14 text
リストを例にしたViewとComposeの違い / Viewの場合 14
Slide 15
Slide 15 text
リストを例にしたViewとComposeの違い / Viewの場合 15
Slide 16
Slide 16 text
🫠 16
Slide 17
Slide 17 text
リストを例にしたViewとComposeの違い / Composeの場合 17
Slide 18
Slide 18 text
リストを例にしたViewとComposeの違い / Composeの場合 18
Slide 19
Slide 19 text
リストを例にしたViewとComposeの違い / Composeの場合 19
Slide 20
Slide 20 text
😊 20
Slide 21
Slide 21 text
Jetpack Composeを導入するメリット ● Kotlinだけで直感的に書けるので読みやすく、早くコードが書ける ● Composeだけで動きまでプレビューできるので、表示する状態さえ定義 すればUIだけ先に作るのが容易にできる ○ 作業分担やレビューもしやすい ● 公式のドキュメントやライブラリが充実している ○ 移行しやすくするためのライブラリもあるので一部からでも導入しや すい 21
Slide 22
Slide 22 text
Jetpack Composeを導入するメリット ● Android Viewはほぼメンテナンスモードであると言えること から、まだJetpack Composeへの移行に着手ができてい ないプロジェクトは早めに移行に着手できると生産性も上 がってよさそう 22
Slide 23
Slide 23 text
Jetpack Composeの導入とルール決め 23
Slide 24
Slide 24 text
● Kotlin Compose Compiler Gradle プラグイン ○ Kotlin 2.0以上で使える ○ Kotlin 2.0未満の場合はJetpack Compose Compilier を使う Jetpack Composeの導入 24 https://developer.android.com/develop/ui/compose/compiler
Slide 25
Slide 25 text
● 依存関係の追加 ○ ドキュメントから必要なものを追加する ○ https://developer.android.com/develop/ui/compose/setup ● プレビューの依存関係が無いとプレビューが出せないので必ず入れま しょう Jetpack Composeの導入 25
Slide 26
Slide 26 text
Jetpack Composeの導入におけるルール決め 26 ● Jetpack Composeはいろいろな書き方ができて自由度が 高い ● ある程度ルールを決めておかないとカオスになるので導入 する前に先に決めておいたほうがよい
Slide 27
Slide 27 text
Jetpack Composeの導入におけるルール決め Compose関連のファイルの置き場を決める テーマやカラーなどを定義する 命名規則を決める 状態を持つ場所を決める プレビューしたい要素を定義する 画面までプレビューできる設計にする 01 02 03 04 05 06 27
Slide 28
Slide 28 text
Jetpack Composeの導入におけるルール決め Compose関連のファイルの置き場を決める テーマやカラーなどを定義する 命名規則を決める 状態を持つ場所を決める プレビューしたい要素を定義する 画面までプレビューできる設計にする 01 02 03 04 05 06 28
Slide 29
Slide 29 text
Compose関連のファイルの置き場を決める ● カラーやテーマ、共通コンポーネントの置き場 ○ Color.kt、Theme.kt、BrandButton.kt etc… ● 画面 / 機能ごとのコンポーネントの置き場 ○ HomeTopAppBar、HomeListItem etc… 29
Slide 30
Slide 30 text
カラーやテーマ、共通コンポーネントの置き場 ● パッケージで分けて置く ● compose専用モジュールに置く 30
Slide 31
Slide 31 text
パッケージで分けて置く ● appモジュール内でパッケージを分けて置く 31
Slide 32
Slide 32 text
パッケージで分けて置く ● appモジュール内でパッケージを分けて置く 32
Slide 33
Slide 33 text
パッケージで分けて置く ● appモジュール内でパッケージを分けて置く ● 一番シンプルで簡単な構成 ● 共通コンポーネントに画面 / 機能依存の処理が入ってしまう可 能性がある 33
Slide 34
Slide 34 text
compose専用モジュールに置く ● compose専用モジュールを作成し、その中にcompose用 の依存関係、カラーやテーマ、共通コンポーネントを置く ● マルチモジュール構成になっていない場合は少しハードル が高い反面、依存関係がはっきりするのでcompose以外 のコードが入る可能性が少ない 34
Slide 35
Slide 35 text
compose専用モジュールに置く ● マルチモジュール構成にするとき、リソースをcomposeモ ジュールに移動したいケースがある ● AGP 8.0以上では android.nonTransitiveRClass がデ フォルトでtrueになっており、trueだとRクラスがモジュール ごとに生成される 35 https://developer.android.com/build/releases/past-releases/agp-8-0 -0-release-notes#default-changes
Slide 36
Slide 36 text
compose専用モジュールに置く ● モジュールごとにRクラスが生成されるので、完全修飾名で リソース呼び出すか、asで別名参照することで短い名前で 呼び出せる ● falseに設定すればRクラスのimportを変えずにリソース ファイルをcomposeモジュールに移動できるが、ビルド時間 が長くなる可能性がある 36
Slide 37
Slide 37 text
compose専用モジュールに置く ● どちらもメリット・デメリットあるので、チームで話し合って方 針を決めましょう 37
Slide 38
Slide 38 text
パッケージの分け方を決める ● パッケージのみでも専用モジュールでも、どこに何のパッケー ジをいれるのかは決めておいたほうがよい ● たとえば ○ ColorやTheme、Fontなど -> themeパッケージ ○ Viewの外部依存ライブラリでComposeに移行できないの でwrapしたいもの -> interopパッケージ 38
Slide 39
Slide 39 text
39 パッケージの分け方を決める https://github.com/PhilJay/MPAndroidChart
Slide 40
Slide 40 text
画面 / 機能ごとのコンポーネントの置き場 ● 画面を構成するのに必要な細かいUIコンポーネント ○ HomeTopAppBar、HomeList、HomeListItem etc… ● UiStateなどの状態クラス ○ HomeUiState、HomeItemState etc… 40
Slide 41
Slide 41 text
パッケージ構成例 ● 画面全体で使うもの ○ 画面のrootパッケージ ● 画面を構成するのに必要な細 かいUIコンポーネント ○ composeパッケージ ● UIコンポーネントで必要な状態 ○ stateパッケージ 41
Slide 42
Slide 42 text
Compose関連のファイルの置き場を決める ● 現在のプロジェクトの構成によって適切なパッケージは違って くると思うので、チームで話し合って決めましょう 42
Slide 43
Slide 43 text
Jetpack Composeの導入におけるルール決め Compose関連のファイルの置き場を決める テーマやカラーなどを定義する 命名規則を決める 状態を持つ場所を決める プレビューしたい要素を定義する 画面までプレビューできる設計にする 01 02 03 04 05 06 43
Slide 44
Slide 44 text
● 既存のxmlテーマを流用する(deprecated) ● 既存のxmlテーマをComposeのMaterialThemeに移 行する ● 独自のテーマを作る ● テーマを作るのをやめる テーマやカラーなどを定義する 44
Slide 45
Slide 45 text
● AppCompatのxmlテーマ ● MaterialDesignのxmlテーマ ● MaterialDesign3のxmlテーマ 既存のxmlテーマを流用する( deprecated) 45
Slide 46
Slide 46 text
● deprecatedかつ代替手段は無いのであまりおすすめはでき ませんが、一応簡単に流用する方法の一つ 既存のxmlテーマを流用する( deprecated) 46 https://github.com/google/accompanist/tree/main/themeadapter-appcompat https://github.com/material-components/material-components-android-compose-theme-adapter/blob/930d1af ccf3cc23193c0a91fa81b55f8d7af3be8/README.md
Slide 47
Slide 47 text
● Material Theme Builderを使って移行する ○ https://material-foundation.github.io/material-the me-builder/ 既存のxmlテーマをComposeのMaterialThemeに移行する 47
Slide 48
Slide 48 text
https://github.com/android/nowinandroid/blob/main/core/designsystem/src/main/kotlin/com/ google/samples/apps/nowinandroid/core/designsystem/theme/Color.kt Color定義の例: NowInAndroidの場合 48
Slide 49
Slide 49 text
https://github.com/android/nowinandroid/blob/main/core/designsystem/src/main/kotlin/com/ google/samples/apps/nowinandroid/core/designsystem/theme/Theme.kt Theme定義の例: NowInAndroidの場合 49
Slide 50
Slide 50 text
● MaterialThemeにそのままマッピングできるカラーが定義さ れていれば問題ないが、結構たいへんな作業になる 既存のxmlテーマをComposeのMaterialThemeに移行する 50
Slide 51
Slide 51 text
● sealed interfaceでカラーを定義 ● 定義したカラーをCompositionLocalで提供 ● システムのテーマ設定に応じてLight / Darkの切り替えを提 供する独自のテーマを作成 ● 独自のテーマを使う 独自のテーマを作る 51
Slide 52
Slide 52 text
52 sealed interfaceで カラーを定義 sealed interfaceにプロパティとしてカ ラーを定義 Light、Darkそれぞれのカラーを定義 する
Slide 53
Slide 53 text
53 定義したカラーを CompositionLocalで 提供 定義したカラーをCompositionLocal で提供できるようにする CompositionLocalのカラーにアクセ スしやすいようにobjectクラスも定義す る
Slide 54
Slide 54 text
54 独自のテーマを作成 システムテーマに応じたカラーが簡単 に提供できるように独自のテーマを作 成
Slide 55
Slide 55 text
55 独自のテーマを使う
Slide 56
Slide 56 text
56 独自のテーマを使う CompositionLocalからカラーを取得 することで、テーマカラーが提供されて いればそれを利用できるようにする
Slide 57
Slide 57 text
57 独自のテーマを使う MikanThemeではシステム設定に応じ てlight / darkを提供しているので、こ れを使うとダークモードの場合はダーク テーマが適用される
Slide 58
Slide 58 text
● テーマで適用するのをやめて個別にカラーを定義する ○ カラーの定義はlight / dark両方に対応できるものを定義 する ● 同じカラーのコンポーネント(Buttonなど)は共通の Composableを作って利用する ○ View時代のCustomViewと違って簡単に作れるので 手間もかからない テーマを作るのをやめる 58
Slide 59
Slide 59 text
テーマを作るのをやめる ● テーマで適用するのをやめて個別にカラーを定義する ○ カラーの定義はlight / dark両方に対応できるものを定義 する ● 同じカラーのコンポーネント(Buttonなど)は共通の Composableを作って利用する ○ View時代のCustomViewと違って簡単に作れるので 手間もかからない 59
Slide 60
Slide 60 text
LightとDarkに対応し たカラーを定義する 60
Slide 61
Slide 61 text
LightとDarkに対応し たカラーを定義する ● lightとdarkのカラーを両方定義で きるenum classを作成 61
Slide 62
Slide 62 text
LightとDarkに対応し たカラーを定義する ● プロジェクトにあるcolors.xmlから カラー定義を移植してくる 62
Slide 63
Slide 63 text
LightとDarkに対応し たカラーを定義する ● darkのデフォルト値をlightにして いるので、まだダークテーマのカ ラー定義がない場合はlightのみ の指定でもOK 63
Slide 64
Slide 64 text
LightとDarkに対応し たカラーを定義する ● カスタムゲッターにComposableアノ テーションをつけて、その中でテーマに 応じたカラーを返せるようにする ● 常にこのsystemを呼び出すことで、あ とでdarkのカラー定義が決まった場合 はこのdarkを設定するだけでダーク テーマ対応ができる 64
Slide 65
Slide 65 text
LightとDarkに対応し たカラーを定義する ● 試しにリストのサンプルコードの色 を変えてみる 65
Slide 66
Slide 66 text
LightとDarkに対応し たカラーを定義する ● @Previewを @PreviewLightDarkに変えるこ とで両テーマのプレビューを確認 できるように変える 66
Slide 67
Slide 67 text
LightとDarkに対応したカラーを定義する 67
Slide 68
Slide 68 text
68 カラースタイルが フォルダでグルーピングさ れている場合 sealed interfaceにしてフォルダと同じ 構造でenum classを作れば、 直感的に色を参照できる
Slide 69
Slide 69 text
テーマを作るのをやめる ● 今回紹介した方法であればカラー定義だけ移行すればいいの で、Jetpack Compose導入のハードルは少し下がるかも 69
Slide 70
Slide 70 text
Jetpack Composeの導入におけるルール決め Compose関連のファイルの置き場を決める テーマやカラーなどを定義する 命名規則を決める 状態を持つ場所を決める プレビューしたい要素を定義する 画面までプレビューできる設計にする 01 02 03 04 05 06 70
Slide 71
Slide 71 text
命名規則を決める ● Composableファイルの命名規則 ● Composable関数の命名規則 ● Previewの命名規則 ● 状態クラスの命名規則 71
Slide 72
Slide 72 text
Composableファイルの命名規則 ● 次のようなComposable関数を作るとき ○ HomeAppBar ○ HomeBookCard ○ HomeBookList ● Composableファイル名をどうするか? ○ suffixにComposableをつける ○ Composable関数名と同じにする 72
Slide 73
Slide 73 text
Composableファイルの命名規則 ● mikanでは最初、ViewとComposableをファイル名で区別し たり検索しやすくするためにsuffixにComposableをつけてい た ● 実際に運用してみたところ... ○ Composableというsuffixはあまりに長すぎる ○ composeというパッケージを作ってその下に各 Composableファイルを入れるほうが分かりやすい 73
Slide 74
Slide 74 text
Composableファイル名の命名規則 74 Composable suffix compose package
Slide 75
Slide 75 text
Composableファイルの命名規則 ● チーム全員が思い思いに作ってしまうと収拾がつかなくなるの で、最初にどのようなファイル命名規則にするのか話し合って 決めておいたほうがよい ● 個人的には関数と同じ名前のファイル名にして、コンポーネン トだけ一つのパッケージにまとめるやり方がおすすめ 75
Slide 76
Slide 76 text
Composable関数の命名規則 ● アプリ内共通で使う関数 ○ アプリ名 + 関数名 ○ ex. MikanTopAppBar ● ある画面や機能で使う関数名 ○ 画面名 + 関数名 ○ ex. HomeTopAppBar 76
Slide 77
Slide 77 text
Composable関数の命名規則 ● Composable関数はどこからでも気軽に呼び出せてしまう ● featureモジュールで分けたりしていない場合、たとえばホーム 画面用に作った関数を検索画面で呼び出して使うといったこと も簡単にできてしまう ● 関数名のprefixで、ある程度どの画面や機能を対象とした関数 なのかを明確にしておくことでそういった問題を防ぐ 77
Slide 78
Slide 78 text
Previewの命名規則 ● MainScreenというComposable関数があったとき ● MainScreenPreviewにするのか、PreviewMainScreenにす るのか ● Previewのパターンに合わせてPreview名を変えるのかなど ○ ex. HomeScreenLoggedInPreview、 HomeScreenNotLoggedInPreview 78
Slide 79
Slide 79 text
状態クラスの命名規則 ● 画面全体の状態 ○ 画面名 + UiState ○ ex. MainScreen → MainUiState ● 特定のComposable内で利用する状態 ○ Composable名 + State ○ ex. MainListItem → MainListItemState 79
Slide 80
Slide 80 text
Jetpack Composeの導入におけるルール決め Compose関連のファイルの置き場を決める テーマやカラーなどを定義する 命名規則を決める 状態を持つ場所を決める プレビューしたい要素を定義する 画面までプレビューできる設計にする 01 02 03 04 05 06 80
Slide 81
Slide 81 text
状態を持つ場所を決める ● UIの状態がComposable内で完結する場合 ● UIの状態を他のComposableとも共有する場合 ● 外部の情報の読み取りや書き込みが必要な状態の場合 81
Slide 82
Slide 82 text
UIの状態がComposable内で完結する場合 ● テキストフィールドの入力中の文字列 ● Composableのoffsetを移動するなどのアニメーション Composable内にrememberな変数を定義して状態を保持する 82
Slide 83
Slide 83 text
UIの状態がComposable内で完結する場合 ● rememberで状態を保持しておくことで、rememberは Composeの再コンポジションの仕組みと連携して動作するた め、必要な場合にのみ再計算が行われる ○ 不要な再計算を避け、パフォーマンスを向上できる 83
Slide 84
Slide 84 text
複雑なアニメーションの場 合 84
Slide 85
Slide 85 text
複雑なアニメーションの場合 85
Slide 86
Slide 86 text
UIの状態がComposable内で完結する場合 ● Composableの引数に状態を渡すと状態の変化を監視し、変 化があるたびに再コンポジションをトリガーする ○ UiStateにアニメーション状態を持つとパフォーマンスの面 で不利になる可能性がある ● アニメーションの一時的な状態保持のようなユースケースでは rememberを使ってComposable内で完結させたほうがよい 86
Slide 87
Slide 87 text
UIの状態を他の Composableとも共有する場合 ● あるボタンを押したらリストをスクロールしたいなど 画面のComposableに状態を持ち、各Composableに状態を渡す 87
Slide 88
Slide 88 text
UIの状態を他の Composableとも共有 する場合 88
Slide 89
Slide 89 text
ScrollToBottomButton MainScreen MainList MainButtons LazyColumn ScrollToTopButton LazyListState UIの状態を他の Composableとも共有する場合 https://developer.android.com/develop/ui/compose/state-hoisting?hl=ja#hoisting-composables 89
Slide 90
Slide 90 text
ScrollToBottomButton MainScreen MainList MainButtons LazyColumn ScrollToTopButton LazyListState LazyListState UIの状態を他の Composableとも共有する場合 90 https://developer.android.com/develop/ui/compose/state-hoisting?hl=ja#hoisting-composables
Slide 91
Slide 91 text
ScrollToBottomButton MainScreen MainList MainButtons LazyColumn ScrollToTopButton LazyListState LazyListState LazyListState LazyListState lazyListState.scrollToItem(list.lastIndex) lazyListState.scrollToItem(0) UIの状態を他の Composableとも共有する場合 91 https://developer.android.com/develop/ui/compose/state-hoisting?hl=ja#hoisting-composables
Slide 92
Slide 92 text
外部の情報の読み取りや書き込みが必要な状態の場合 ● APIやDBなどから取得して画面に表示 ● テキスト入力などのユーザーのアクションからAPIやDBなどに 保存する 画面のUI状態(UiState)として持つ 92
Slide 93
Slide 93 text
UiStateに持つ情報 ● APIから取得して表示する情報 ○ 商品名、商品説明、商品画像URL etc… ● APIに渡したい情報 ○ 商品のid、ユーザーが入力した内容(入力確定した情報) etc… ● ローディングやエラーなどの情報 93
Slide 94
Slide 94 text
ローディングやエラーなどの情報 ● UiStateにどのように持つ? ○ UiState自体をsealed interfaceにして、Loadingなどの 状態を表す ○ UiStateの中に一つの変数としてisLoadingやエラー用の 状態を持つ ○ UiStateの中に各状態を表す変数を持つ 94
Slide 95
Slide 95 text
DroidKaigi/conference-app-2024の場合 https://github.com/DroidKaigi/conference-app-2024/blob/main/feature/sessions/src/com monMain/kotlin/io/github/droidkaigi/confsched/sessions/TimetableItemDetailScreen.kt 95
Slide 96
Slide 96 text
mikan Androidの場合 96
Slide 97
Slide 97 text
mikan Androidの場合 97
Slide 98
Slide 98 text
Giftmall Androidの場合 https://github.com/mercari/RemoteDataK 98
Slide 99
Slide 99 text
Giftmall Androidの場合 https://github.com/mercari/RemoteDataK 99
Slide 100
Slide 100 text
ローディングやエラーなどの情報 ● UiStateにどのように持つのか、やり方は色々ありますがプロ ジェクト全体で統一するのが大事だと思うので、あらかじめ方 針を決めておきましょう 100
Slide 101
Slide 101 text
UiStateはどこに持つ? ● よくあるのはLifecycle ViewModelに持つ ● UiStateの初期化処理をViewModelで担いつつ、 ViewModelのライフサイクルで画面の状態を持てるので画面 回転などの変更にも影響されない ● どこに持つかは各アプリの設計にもよるので、設計に合わせ て適切な場所に持つのがよい 101
Slide 102
Slide 102 text
Jetpack Composeの導入におけるルール決め Compose関連のファイルの置き場を決める テーマやカラーなどを定義する 命名規則を決める 状態を持つ場所を決める プレビューしたい要素を定義する 画面までプレビューできる設計にする 01 02 03 04 05 06 102
Slide 103
Slide 103 text
プレビューしたい要素を定義する ● androidx.compose.ui:ui-tooling-preview 1.6.0-alpha01 以降ではマルチプレビューが導入されている ● マルチプレビューは複数のプレビューを同時に表示できる 103
Slide 104
Slide 104 text
プレビューしたい要素を定義する ● Composeに標準で用意されているもの ○ マルチプレビュー テンプレート ● Previewを自分でカスタマイズする ○ カスタム マルチプレビュー アノテーション 104
Slide 105
Slide 105 text
Composeに標準で用意されているもの シンプルなプレビュー @Preview Light / Darkのプレビュー @PreviewLightDark フォントサイズごとのプレビュー @PreviewFontScale スクリーンサイズごとのプレビュー @PreviewScreenSizes ダイナミック カラーごとのプレビュー @PreviewDynamicColors 105
Slide 106
Slide 106 text
@PreviewScreenSizes スクリーンサイズごとのプレビュー Phone、Phone - Landscape、 Tablet、Unfolded Foldable、 Desktop 106
Slide 107
Slide 107 text
UI Check Mode ● UI Check Modeという機能があるので、 @PreviewScreenSizes は代わりにUI Check Modeを使う でもいいかも 107
Slide 108
Slide 108 text
UI Check Mode 108
Slide 109
Slide 109 text
Previewを自分で カスタマイズする ● カスタム マルチプレビュー アノテーション ● Previewアノテーションにある引 数を好きに組み合わせる 109
Slide 110
Slide 110 text
Previewを自分で カスタマイズする ● 背景色を表示 ● 英語と日本語を表示 ● ダークとライトを表示 110
Slide 111
Slide 111 text
Previewを自分でカスタマイズする 111
Slide 112
Slide 112 text
Jetpack Composeの導入におけるルール決め Compose関連のファイルの置き場を決める テーマやカラーなどを定義する 命名規則を決める 状態を持つ場所を決める プレビューしたい要素を定義する プレビューしやすい設計にする 01 02 03 04 05 06 112
Slide 113
Slide 113 text
プレビューしやすい設計にする ● Composable関数の粒度 ● Composable関数の引数 ● Previewのスコープを決める ● プレビューしやすくするための工夫 ● 目指すべきところを決めて設計を考える 113
Slide 114
Slide 114 text
Composable関数の粒度 ● すべてを1つのComposable関数にまとめることはできるが、 それをやると巨大なComposable関数ができてしまう ● ある程度分けて作ったほうが細かい単位でPreviewを確認で きるし、メンテナンスもしやすい 114
Slide 115
Slide 115 text
Composable関数の粒度 ※関数の分け方のイメージです115
Slide 116
Slide 116 text
Composable関数の粒度 MainTopAppBar() 116 ※関数の分け方のイメージです
Slide 117
Slide 117 text
Composable関数の粒度 MainList() { } MainTopAppBar() 117 ※関数の分け方のイメージです
Slide 118
Slide 118 text
Composable関数の粒度 MainList() { MainListItem() MainListItem() … } MainTopAppBar() 118 ※関数の分け方のイメージです
Slide 119
Slide 119 text
Composable関数の粒度 MainList() { MainListItem() MainListItem() … } MainBottomAppBar() MainTopAppBar() 119 ※関数の分け方のイメージです
Slide 120
Slide 120 text
Composable関数の粒度 MainList() { MainListItem() MainListItem() … } MainBottomAppBar { BottomAppBarItem() BottomAppBarItem() BottomAppBarItem() } MainTopAppBar() 120 ※関数の分け方のイメージです
Slide 121
Slide 121 text
Composable関数の引数 ● 状態 ● コールバック関数 121
Slide 122
Slide 122 text
Composable関数の引数 / 状態 ● 基本的には画面のUiStateを受け取るのはScreenだけで よい ● 各コンポーネントはそのコンポーネントで必要な値のみを受 け取る 122
Slide 123
Slide 123 text
123
Slide 124
Slide 124 text
124
Slide 125
Slide 125 text
Composable関数の引数 / 状態 ● 必要な値のみを受け取り、画面のUiStateに依存しないよう にしておくと、画面のUiStateがなくても各コンポーネントの UIを作成できる ● もし後から共通コンポーネントにしたくなった場合でも、ファ イルの移動や関数名の変更だけでよくなるので再利用性が 高くなる 125
Slide 126
Slide 126 text
Composable関数の引数 / コールバック関数 ● コールバック関数は一つずつ引数にとる ● Viewのときの感覚でinterfaceにコールバックをまとめるよ うな実装はしなくてもよい ○ 無名objectに実装したりなど余計な手続きが増えるの でプレビューも実装しづらくなる 126
Slide 127
Slide 127 text
Composable関数の引数 / コールバック関数 127
Slide 128
Slide 128 text
Composable関数の引数 / コールバック関数 ● 画面に機能が多ければ多いほどコールバック関数の引数 が増えるのでは...? ○ それはそう ● sealed interfaceにイベントをまとめて、一つのコールバッ ク関数で複数のイベントに対応するようなアプローチもある 128
Slide 129
Slide 129 text
https://engineering.teknasyon.com/stop-passing-event-ui-action-cal lbacks-in-jetpack-compose-a4143621c365 129
Slide 130
Slide 130 text
130 https://engineering.teknasyon.com/stop-passing-event-ui-action-cal lbacks-in-jetpack-compose-a4143621c365
Slide 131
Slide 131 text
131 https://engineering.teknasyon.com/stop-passing-event-ui-action-cal lbacks-in-jetpack-compose-a4143621c365
Slide 132
Slide 132 text
Composable関数の引数 / コールバック関数 ● 1つの画面にコールバック関数が大量にあるケースがたくさ んあるのであれば、UiActionにまとめる方式のほうがスッ キリしていいかもしれない ● 逆にコールバック関数が少ない画面のほうが多い場合だと 処理が冗長になってコードが追いづらくなる可能性がある 132
Slide 133
Slide 133 text
Composable関数の引数 / コールバック関数 ● どのやり方にするにしてもプロジェクト全体で統一しておい たほうが読みやすいので、チームで話し合って方針を決め ておくとよいでしょう 133
Slide 134
Slide 134 text
Previewのスコープを決める ● privateにするのか、publicのままにしておくのか決める ● Composable関数はどこからでも参照できるため、Previewを publicにしていると入力補完に出てくるので 不要ならprivateにしたほうがよい 134
Slide 135
Slide 135 text
Previewのスコープを決める ● もしプレビューの関数をそのままテストでも利用したい場合は publicにする必要がある ○ すべてにテストを書く想定ならすべてpublicでもよさそう 135
Slide 136
Slide 136 text
Previewのスコープを決める ● もし一部のみUIテストを書く場合はテストの都合でpublicに なっていることを明確にするためにAndroidXの @VisibleForTestingをつける ○ ビルドエラーにはならないがテスト以外で利用しようとする とlintエラーが出る 136
Slide 137
Slide 137 text
プレビューしやすくするための工夫 ● UiStateや描画に必要な情報(ModifierやPaddingValuesなど)、 コールバック関数を受け取るScreenのComposable関数 を作る ● コールバック関数にはデフォルト引数を入れる ● 複数パターンのプレビューの場合は無理にプレビュー関数 を分けなくてもいい 137
Slide 138
Slide 138 text
UiStateとコールバック関 数を受け取る Screenの Composable関数を作 る 138
Slide 139
Slide 139 text
UiStateとコールバック関 数を受け取る Screenの Composable関数を作 る UiStateを生成するだけで自由に 画面のプレビューを作れるので、 パターンごとにプレビューを確認 しやすい 139
Slide 140
Slide 140 text
コールバック関数には デフォルト引数を入れる 140
Slide 141
Slide 141 text
コールバック関数には デフォルト引数を入れる プレビュー時に不要なコールバック 関数は設定しなくてもよくなる 141
Slide 142
Slide 142 text
複数パターンのプレビューの 場合は無理にプレビュー関 数を分けなくてもいい ● 未ログイン ● ログイン済 ● ログイン済かつ通知カウントを表 示 142
Slide 143
Slide 143 text
複数パターンのプレビューの 場合は無理にプレビュー関 数を分けなくてもいい ● 未ログイン ● ログイン済 ● ログイン済かつ通知カウントを表 示 143
Slide 144
Slide 144 text
複数パターンのプレビューの 場合は無理にプレビュー関 数を分けなくてもいい こんな感じでプレビュー用に好きに レイアウトを組んでしまってもOK 144
Slide 145
Slide 145 text
複数パターンのプレビューの 場合は無理にプレビュー関 数を分けなくてもいい 145
Slide 146
Slide 146 text
目指すべきところを決めて設計を考える ● UIの描画だけComposeに移行する ● Full Composeを目指す 146
Slide 147
Slide 147 text
UIの描画だけ Composeに移行する ● UIの描画だけComposable関数にする ● コールバック関数はActivity / Fragmentで実装する ● UIだけCompose化するならこのやり方のほうがViewか らの置き換えがしやすいのでおすすめ ○ mikanでは今のところこのやり方でやっています 147
Slide 148
Slide 148 text
UIの描画だけ Composeに移行する 148
Slide 149
Slide 149 text
UIの描画だけ Composeに移行する 149
Slide 150
Slide 150 text
UIの描画だけ Composeに移行する ● いきなり画面全体をCompose化するのは難しいが、一部 Compose化したいケースもある ● たとえばデバッグメニューをドロワーに隠していて、ドロ ワーの中身はCustomViewで書かれているような場合、 Compose化したほうがデバッグメニューをメンテしやすく なる 150
Slide 151
Slide 151 text
151 CustomViewの中を Compose化する AbstractComposeViewを継承し たクラスを作成し、Content関数内 でComposeの処理を書く
Slide 152
Slide 152 text
152 CustomViewの中を Compose化する これでCompose化すると、xmlレイ アウトの中にCompose化された CustomViewを埋め込める
Slide 153
Slide 153 text
153 CustomViewの中を Compose化する 一応ComposeViewでも似たようなこと はできる
Slide 154
Slide 154 text
154 CustomViewの中を Compose化する ComposeViewの場合はActivityや Fragment側でComposeの処理を書く 必要がある CustomViewの中だけで処理をするの が難しい場合はこれでもOK
Slide 155
Slide 155 text
Full Composeを目指す ● すべてをComposable関数の中で行う ○ ViewModelのinject ○ Fragment → Screen ○ Jetpack Navigation → Navigation with Compose 155
Slide 156
Slide 156 text
ViewModelのinjectをScreenで行う 156
Slide 157
Slide 157 text
Navigation with Compose 1. 一旦Jetpack NavigationをNavigation with Compose に移行 ○ fragment-composeにあるAndroidFragment でFragmentをそのままComposable関数内で呼び 出す 157
Slide 158
Slide 158 text
Navigation with Compose 2. Fragmentの中のUI実装を順次Composeに移行 3. AndroidFragmentで呼び出しているものを順次Screen Composableに置き換え 4. 最終的にFragmentからも卒業 158
Slide 159
Slide 159 text
Jetpack Navigation 159
Slide 160
Slide 160 text
※画像はイメージです Navigation with Compose 160
Slide 161
Slide 161 text
※画像はイメージです Navigation with Compose 161
Slide 162
Slide 162 text
※画像はイメージです Navigation with Compose 162
Slide 163
Slide 163 text
実例を元にした Jetpack Composeへの移行 163
Slide 164
Slide 164 text
今回移行する画面 mikanのオンボーディング画面 164
Slide 165
Slide 165 text
Jetpack Composeへの移行 移行対象のレイアウトを確認する 移行対象の画面のレイアウトをコンポーネント単位に分割する 移行に必要なタスクを洗い出す UIを実装する UiStateを定義する プレビューで実際の動作に近い画面の動きを実現する ロジックを実装する ViewからComposeに置き換える 01 02 03 04 05 06 07 08 165
Slide 166
Slide 166 text
移行対象のレイアウトを確認する 166
Slide 167
Slide 167 text
移行対象のレイアウトを確認する 167
Slide 168
Slide 168 text
Jetpack Composeへの移行 移行対象のレイアウトを確認する 移行対象の画面のレイアウトをコンポーネント単位に分割する 移行に必要なタスクを洗い出す UIを実装する UiStateを定義する プレビューで実際の動作に近い画面の動きを実現する ロジックを実装する ViewからComposeに置き換える 01 02 03 04 05 06 07 08 168
Slide 169
Slide 169 text
移行対象の画面のレイアウトをコンポーネント単位に分割する 169
Slide 170
Slide 170 text
移行対象の画面のレイアウトをコンポーネント単位に分割する TopAppBarとして作れそう 170
Slide 171
Slide 171 text
移行対象の画面のレイアウトをコンポーネント単位に分割する タイトル、メッセージ、画像だけ変えれば 1つのコンポーネントとして作れそう 171
Slide 172
Slide 172 text
移行対象の画面のレイアウトをコンポーネント単位に分割する ニックネーム入力欄として作れそう 172
Slide 173
Slide 173 text
ジャンル選択のUIとして作れそう 移行対象の画面のレイアウトをコンポーネント単位に分割する 173
Slide 174
Slide 174 text
移行対象の画面のレイアウトをコンポーネント単位に分割する 目標、実力の選択UI として作れそう 174
Slide 175
Slide 175 text
移行対象の画面のレイアウトをコンポーネント単位に分割する 1日の学習単語数選択の UIとして作れそう 175
Slide 176
Slide 176 text
移行対象の画面のレイアウトをコンポーネント単位に分割する リマインダー設定のUI として作れそう 176
Slide 177
Slide 177 text
移行対象の画面のレイアウトをコンポーネント単位に分割する フリートライアル開始のUI として作れそう 177
Slide 178
Slide 178 text
Jetpack Composeへの移行 移行対象のレイアウトを確認する 移行対象の画面のレイアウトをコンポーネント単位に分割する 移行に必要なタスクを洗い出す UIを実装する UiStateを定義する プレビューで実際の動作に近い画面の動きを実現する ロジックを実装する ViewからComposeに置き換える 01 02 03 04 05 06 07 08 178
Slide 179
Slide 179 text
移行に必要なタスクを洗い出す ● 分割したコンポーネントのUI実装に加えてロジック実装も 含めて必要なタスクを洗い出す ● タスクの洗い出しができれば、UI実装についてはチーム メンバーで手分けして並行して進められる 179
Slide 180
Slide 180 text
移行に必要なタスクを洗い出す / UI実装 ● AppBarとプログレス ● メッセージとアイコン ● ニックネーム入力 ● ジャンル選択 ● 目標、実力選択 ● 1日の学習単語数選択 ● リマインダー設定 ● フリートライアル開始 ● オンボーディング画面 ○ UiState実装 180
Slide 181
Slide 181 text
移行に必要なタスクを洗い出す / UI実装 ● AppBarとプログレス ● メッセージとアイコン ● ニックネーム入力 ● ジャンル選択 ● 目標、実力選択 ● 1日の学習単語数選択 ● リマインダー設定 ● フリートライアル開始 ● オンボーディング画面 ○ UiState実装 181
Slide 182
Slide 182 text
移行に必要なタスクを洗い出す / UI実装 ● AppBarとプログレス ● メッセージとアイコン ● ニックネーム入力 ● ジャンル選択 ● 目標、実力選択 ● 1日の学習単語数選択 ● リマインダー設定 ● フリートライアル開始 ● オンボーディング画面 ○ UiState実装 182
Slide 183
Slide 183 text
移行に必要なタスクを洗い出す / その他実装 ● ViewModelのロジック実装 ● オンボーディング画面のイベント実装 ● View関連のコードの削除 183
Slide 184
Slide 184 text
Jetpack Composeへの移行 移行対象のレイアウトを確認する 移行対象の画面のレイアウトをコンポーネント単位に分割する 移行に必要なタスクを洗い出す UIを実装する UiStateを定義する プレビューで実際の動作に近い画面の動きを実現する ロジックを実装する ViewからComposeに置き換える 01 02 03 04 05 06 07 08 184
Slide 185
Slide 185 text
UIを実装する ● AppBarとプログレス ● メッセージとアイコン ● ニックネーム入力 ● ジャンル選択 ● 目標、実力選択 ● 1日の学習単語数選択 ● リマインダー設定 ● フリートライアル開始 ● オンボーディング画面 ○ UiState実装 185
Slide 186
Slide 186 text
UIを実装する / TopAppBar 186
Slide 187
Slide 187 text
UIを実装する TopAppBar まずは単なるProgressBarを作る 187
Slide 188
Slide 188 text
UIを実装する TopAppBar まずは単なるProgressBarを作る 188
Slide 189
Slide 189 text
UIを実装する TopAppBar ステップごとにProgressを進められる ようにする 189
Slide 190
Slide 190 text
UIを実装する TopAppBar ステップごとにProgressを進められる ようにする 190
Slide 191
Slide 191 text
UIを実装する TopAppBar ステップごとにProgressを進められる ようにする 191
Slide 192
Slide 192 text
UIを実装する TopAppBar 最初のステップ以外は戻るボタンが出 るようにする 192
Slide 193
Slide 193 text
UIを実装する TopAppBar 最初のステップ以外は戻るボタンが出 るようにする 193
Slide 194
Slide 194 text
UIを実装する TopAppBar 最初のステップ以外は戻るボタンが出 るようにする 194
Slide 195
Slide 195 text
UIを実装する TopAppBar 必要なパターンのプレビューを作る 195
Slide 196
Slide 196 text
UIを実装する ● こんな感じですべてのUIコンポーネントを実装してい く (UI実装の過程でハマったことがあれば忘れないうちにアウトプットする) 196
Slide 197
Slide 197 text
https://mikan-tech.hatenablog.jp/entry/2023/05/29/113743 197
Slide 198
Slide 198 text
Jetpack Composeへの移行 移行対象のレイアウトを確認する 移行対象の画面のレイアウトをコンポーネント単位に分割する 移行に必要なタスクを洗い出す UIを実装する UiStateを定義する プレビューで実際の動作に近い画面の動きを実現する ロジックを実装する ViewからComposeに置き換える 01 02 03 04 05 06 07 08 198
Slide 199
Slide 199 text
● UiStateの定義の際、必要に応じて画面に必要なクラスを 定義する UiStateを定義する 199
Slide 200
Slide 200 text
Jetpack Composeへの移行 移行対象のレイアウトを確認する 移行対象の画面のレイアウトをコンポーネント単位に分割する 移行に必要なタスクを洗い出す UIを実装する UiStateを定義する プレビューで実際の動作に近い画面の動きを実現する ロジックを実装する UIとロジックを結合する 01 02 03 04 05 06 07 08 200
Slide 201
Slide 201 text
プレビューで実際の動作に近い画面の動きを実現する ● UiStateとコールバック関数を受け取るScreenを作る ● クリックなどのイベントが発生したときに期待するUiStateの状 態変化をPreviewを使ってできる限り表現する 201
Slide 202
Slide 202 text
プレビューで実際の 動作に近い画面の動き を実現する まずは普通にScreenを作る Screenの中の実装は割愛 202
Slide 203
Slide 203 text
プレビューで実際の 動作に近い画面の動き を実現する プレビュー内にrememberで UiStateを保持できるようにする 203
Slide 204
Slide 204 text
プレビューで実際の 動作に近い画面の動き を実現する プレビューの中で各イベントを実装 する 204
Slide 205
Slide 205 text
プレビューで実際の 動作に近い画面の動き を実現する プレビューの中で各イベントを実装 する 205
Slide 206
Slide 206 text
プレビューで実際の 動作に近い画面の動き を実現する Interactive ModeかRun Preview で動作を確認してみる 206
Slide 207
Slide 207 text
プレビューで実際の 動作に近い画面の動き を実現する プレビューで全パターン網羅してお くと便利 207
Slide 208
Slide 208 text
Jetpack Composeへの移行 移行対象のレイアウトを確認する 移行対象の画面のレイアウトをコンポーネント単位に分割する 移行に必要なタスクを洗い出す UIを実装する UiStateを定義する プレビューで実際の動作に近い画面の動きを実現する ロジックを実装する ViewからComposeに置き換える 01 02 03 04 05 06 07 08 208
Slide 209
Slide 209 text
ロジックを実装する ● 期待する状態変化はPreviewで実現しているので、 ○ イベント発火による状態変化の処理をViewModelなど UiStateを持つところに実装する ○ DataSourceから取得したデータをUiStateに変換する処 理を実装する 209
Slide 210
Slide 210 text
Jetpack Composeへの移行 移行対象のレイアウトを確認する 移行対象の画面のレイアウトをコンポーネント単位に分割する 移行に必要なタスクを洗い出す UIを実装する UiStateを定義する プレビューで実際の動作に近い画面の動きを実現する ロジックを実装する ViewからComposeに置き換える 01 02 03 04 05 06 07 08 210
Slide 211
Slide 211 text
ViewからComposeに置き換える ● ActivityやFragmentに実装されているView関連の処理を Composeに置き換える 211
Slide 212
Slide 212 text
ViewからCompose に置き換える ComposeViewを使ってapplyの中で setContentを呼び出す setContentの中はComposeの世界 なのでこの中でScreenなどを呼び出す 212
Slide 213
Slide 213 text
ViewからCompose に置き換える fragment-composeに content拡張関数があるので、こ れを使ってもOK https://developer.android.com/jetpack/androidx/releases/fragment 213
Slide 214
Slide 214 text
ViewからComposeに置き換える ● 置き換えができたらView関連の処理やxmlレイアウトなどを 削除して終わり 214
Slide 215
Slide 215 text
Jetpack Compose移行できた 🎉🎉 215
Slide 216
Slide 216 text
今回紹介した移行方法のメリット ● UIの実装は完全に新規の実装になるので、他の機能開 発の差分に影響しない ○ 空いた時間で少しずつ進められる ● UIコンポーネントの実装はチームメンバーで並行して進 められる 216
Slide 217
Slide 217 text
今回紹介した移行方法のメリット ● Previewで実際の動きに近い動作が再現できるので、 ○ Previewを作り込んでいればViewModelなどでのState の更新処理はPreviewからのほぼコピペで済むケースも ある ○ Composeの実装が終わればロジックの繋ぎ込みはそん なに時間がかからない 217
Slide 218
Slide 218 text
Jetpack Compose移行やっていきましょう 218
Slide 219
Slide 219 text
Android Viewから Jetpack Composeへ 〜Jetpack Compose移行のすゝめ〜 Taichi Sato / @syarihu Giftmall, Inc. / Mikan, Inc. Android Engineer 219