Upgrade to Pro — share decks privately, control downloads, hide ads and more …

トップ画面のRecyclerViewにComposeを一部導入した話/ #nikkei_tech_talk

トップ画面のRecyclerViewにComposeを一部導入した話/ #nikkei_tech_talk

2023/06/15開催、NIKKEI Tech Talk #8登壇資料です #nikkei_tech_talk
タイトルは「トップ画面のRecyclerViewにComposeを一部導入した話」です
https://nikkei.connpass.com/event/284090/

More Decks by 日本経済新聞社 エンジニア採用事務局

Other Decks in Technology

Transcript

  1. ハッシュタグ #nikkei_tech_talk 自己紹介 2 • 2020 年 4 月に日経入社(新卒 4

    年目) • Android チームで日経電子版と紙面ビューアーを開発 • ラーメンとカレーと日本酒が好き
  2. ハッシュタグ #nikkei_tech_talk • 基本的な RecyclerView の仕組み ◦ onBindViewHolder 、onCreateViewHolder のタイミング

    • Compose の LazyColumn 、 LazyRow の仕組み • 複雑な UI を Compose で記述する方法 今日話さないこと 4
  3. ハッシュタグ #nikkei_tech_talk 電子版トップ画面の特徴 9 • ユーザーの PV 数が最も多い画面の 1 つ

    • 重要度の高いニュースが並ぶ • 変更頻度が高い • RecyclerView の Adapter で管理する表示アイテムが 20 種 類近くある ◦ 記事、バナー、ヘッダー、広告など
  4. ハッシュタグ #nikkei_tech_talk 11 Android View RecyclerView Android View Android View

    ComposeView RecyclerView Android View Android View ComposeView LazyColumn ComposeView ComposeView 導入前 部分的導入 完全移行 Jetpack Compose の導入ステップ
  5. ハッシュタグ #nikkei_tech_talk Jetpack Compose の導入ステップ 12 Android View RecyclerView Android

    View Android View ComposeView RecyclerView Android View Android View ComposeView LazyColumn ComposeView ComposeView 紹介 導入前 部分的導入 完全移行
  6. ハッシュタグ #nikkei_tech_talk Compose 導入前の構成 13 • ViewBinding + ListAdapter の構成

    ◦ Groupie や Epoxy などのサードパーティライブラリを使わない素 の RecyclerView • 表示アイテムのレイアウトはすべて xml で記述 ◦ 20種類近くの表示アイテムあり
  7. ハッシュタグ #nikkei_tech_talk Compose 部分的導入後の構成 14 • ViewBinding + AbstractComposeView +

    ListAdapter の 構成 • 表示アイテムのレイアウトは xml または Compose で記述 ◦ 20種類近くの表示アイテムあり • アイテム管理などの仕組みは RecyclerView を踏襲
  8. ハッシュタグ #nikkei_tech_talk なぜ部分的に導入したか 15 • Full Compose な画面に一括で変更するには工数がかかる • 複雑な

    UI のアイテムも多く、変更頻度が高い画面である ◦ 20種類近くの UI パーツがある ◦ 最も訪問される画面なのでリスクが高い • Compose の技術潮流に乗るべく、安全に導入して少しずつ移 行したい • 作った UI を別の Compose な画面において再利用可能
  9. ハッシュタグ #nikkei_tech_talk 事前準備 〜ライブラリアップデート〜 17 • Compose UI のバージョンを 1.2.0

    以上に上げる • RecyclerView のバージョンを 1.3.0 以上に上げる • dependencies にて下記記述 implementation "androidx.compose.ui:ui:1.2.0" implementation "androidx.recyclerview:recyclerview:1.3.0"
  10. ハッシュタグ #nikkei_tech_talk 事前準備 〜ライブラリアップデート〜 18 • RecyclerView と Compose が協調して動作するようになり、

    View が画面外に移動したときの再利用が効率的になる • 詳しくは各ライブラリのリリースノート👇 ◦ https://developer.android.com/jetpack/androidx/releases/compose- ui#1.2.0-alpha06 ◦ https://developer.android.com/jetpack/androidx/releases/recyclervie w#recyclerview-1.3.0-beta01
  11. ハッシュタグ #nikkei_tech_talk 事前準備 〜ライブラリアップデート〜 19 • Compose UI 1.2.0 ◦

    RecyclerView などの PoolingContainer を適切に処理する DisposeOnDetachedFromWindowOrReleasedFromPool と いう新しい ViewCompositionStrategy が追加された • RecyclerView 1.3.0 ◦ PoolingContainer の修正など、Compose UI 1.2.0 に対応した View 再利用の実装
  12. ハッシュタグ #nikkei_tech_talk 実装方法 〜 ViewHolder の作成 〜 20 • ViewBinding

    用の ViewHolder と Compose 用の ViewHolder • Compose 用 のViewHolder には AbstractComposeView を渡す class ViewBindingHolder( val binding: ViewBinding, ) : RecyclerView.ViewHolder(binding.root) class ComposeViewHolder( val abstractComposeView: AbstractComposeView, ) : RecyclerView.ViewHolder(abstractComposeView)
  13. ハッシュタグ #nikkei_tech_talk 実装方法 21 • onCreateViewHolder で ViewType に応じて ViewBindingHolder

    or ComposeViewHolder を返す override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { return when (ViewType.from(viewType)) { ViewType.HEADLINE_ARTICLE -> { val layoutInflater = LayoutInflater.from(parent.context) ViewBindingHolder(ArticleBinding.inflate(layoutInflater, parent, false)) } ViewType.HEADLINE_HEADER -> { ComposeViewHolder(HeaderComposeView(parent.context)) } } }
  14. ハッシュタグ #nikkei_tech_talk 実装方法 22 • onBindViewHolder で型チェックをする • ViewBindingHolder or

    ComposeViewHolder で bind 処理を実行する override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { val item = getItem(position) when (holder) { is ViewBindingHolder -> holder.bind(item) is ComposeViewHolder -> holder.bind(item) } }
  15. ハッシュタグ #nikkei_tech_talk 実装方法 23 • ヘッダーの ComposeView に定義した bind メソッドを呼ぶ

    fun ComposeViewHolder.bind(item: NewsItem) { // Composeで実装されているRecyclerViewのアイテムがbind対象 when (item) { is NewsItem.HeadlineHeader -> { (abstractComposeView as HeaderComposeView).bind(item.headerItem) } is NewsItem.HeadlineBanner -> { (abstractComposeView as BannerComposeView).bind(item.bannerItem) } else -> {} } }
  16. ハッシュタグ #nikkei_tech_talk class HeaderComposeView @JvmOverloads constructor( context: Context, attrs: AttributeSet?

    = null, defStyle: Int = 0, ) : AbstractComposeView(context, attrs, defStyle) { private var headerItem by mutableStateOf<HeaderItem?>(null) @Composable override fun Content() { AppTheme { Header( headerItem = headerItem, ) } } fun bind(headerItem: HeaderItem) { this.headerItem = headerItem } } 実装方法 〜 ヘッダーの ComposeView 〜 24 Content() の中で Composable 関数が書ける
  17. ハッシュタグ #nikkei_tech_talk 実装方法 25 @Composable fun Header( headerItem: HeaderItem?, modifier:

    Modifier = Modifier, ) { // Text、Button、RowなどのComposable関数を自由に書いてレイアウトを作成する } トップ画面以外にも レイアウトを流用できる
  18. ハッシュタグ #nikkei_tech_talk まとめ 26 • RecyclerView に Compose を導入していく方法を紹介しまし た

    • 部分的な導入は実装コストも低いのでぜひ皆さんもやっていきま しょう💪 • Compose を例としてモダンな技術をプロダクトに入れていく チームの流れがあり、楽しい開発ができます!