複雑な画面をRecyclerViewで作るEpoxy - Android
Androidで複雑な画面を作る際に、Airbnbが提供しているEpoxy
というライブラリが良い感じだったので紹介します。
この記事はShibuya.apk
で発表した、今更ながらEpoxy
を元に書いています。
見ていただけるとありがたいです。
今更ながらEpoxy / Lateinit Epoxy - Speaker Deck
また、Epoxyを触った際に作ったレポジトリもGithubで公開しているので、そちらも参考にしてください。
Epoxyとは?
- 複雑な画面を
RecyclerView
で作るためのライブラリ Airbnb
が作成している- 今でも結構活動している
- 2016/08/25に初リリース
androidx
対応
ざっくり言うと、各Section(Row)毎のレイアウトを組んで順番に並べれば、それをRecyclerView
の再利用性を活用しつつ簡単に表示してくれる。というものです。
Airbnbが紹介記事を出しているので、是非こちらをお読みください。
似たようなものとして、最近ではGroupieも有名です。自分はGroupieを使ったことがないので比較については書きません。
AirbnbはEpoxyの他にも、MvRx、Pairsなどのライブラリを出していて、それらとの相性も良いです。
メリット
- 簡潔に複雑な画面を作ることができる
RecyclerView
を使うことによって、各Viewが表示される直前にロードされるためパフォーマンスが良い- Viewの状態を保存しているので、
Carousel
などのスクロール位置を画面外にいっても覚えている - 差分更新によって滑らかなアニメーションが実現できる
大まかな流れ
・セルのレイアウトを作成
.xml
, .kt
どちらからでも作成可能ですが、今回はより楽なXML
ファイルとdatabinding
を用いた方法で作ってみます。.kt
で作成したい場合はこちらを参考にしてください。
・セルからEpoxyModel
を自動生成
作成したセルをpackage-info.java
内で指定することで、EpoxyRecyclerView
で使用するEpoxyModel
をXML
(.kt
)から自動生成します。
・Controllerを作成
RecyclerView
でいう所のAdapter
となる、Controller
を作成します。ここで、各セルの表示順序、データ、OnClick()
等を設定します。
・EpoxyRecyclerView
にController
をセット
最後に、EpoxyRecyclerView
をViewに追加して、Controller
をセットして完成です。
導入
それでは実際に以下のような画面を作ってみたいと思います。
1. 準備
app build.gradle
apply plugin: 'kotlin-kapt' kapt { correctErrorTypes = true }
dependencies { implementation 'com.airbnb.android:epoxy:3.x.y' implementation 'com.airbnb.android:epoxy-databinding:3.x.y' kapt 'com.airbnb.android:epoxy-processor:3.x.y' }
x
, y
には最新のバージョンを入れてください。(投稿時は3.3.0
)
package-info.java
EpoxyModel
をどのXML
ファイルから作るか記述するものです。XML
ファイルのみでdatabinding
を使用してEpoxyModel
を作る際に必要になります。
.kt (or .java)
ファイルを置くフォルダの直下に作成してください。
@EpoxyDataBindingLayouts({R.layout.header_view, ... // other layouts }) package com.airbnb.epoxy.sample; import com.airbnb.epoxy.EpoxyDataBindingLayouts;
これは下記のように接頭辞での指定も可能です。
@EpoxyDataBindingPattern(rClass = R.class, layoutPrefix = "view_holder") package com.example.package;
2. セルの準備
これは特に変わったことはありません。databinding
を使ってレイアウトを組む時と同じです。双方向のdatabindingは出来ないことに注意しましょう。
3. Controllerの作成
Controller
を作成する際はTypedEpoxyController<T>
クラスを継承します。このT
は中身のデータの型です。二つ渡したいときはTyped2EpoxyController<T, U>
というクラスがあります。詳しくはこちらに記載されています。
Controller
でやることは、buildModels(data:)
を実装することです。コードをみた方が早いと思うので今回作ったものを載せます。
2. セルの準備
で作成したレイアウトをpackage-info.javaに記載後、BuildするとEpopxyModel
が自動生成されます。
名前は、
R.layout.~による指定 -> そのまま ex) R.layout.epoxy_header_view -> EpoxyHeaderViewBindingModel_.java
接頭辞による指定 -> 接頭辞以降 ex) (layoutPrefix = "epoxy") R.layout.epoxy_header_view -> HeaderViewBindingModel_.java
といったようになります。
これを用いてbuildModels(data:)
に記述していくのですが、便利なExtensionがあり、次のように記述出来ます。(今の所は...)
ここでid
はViewを一意に判断するための識別子で、その後のciryName
, description
がXML
ファイルで書いた<data />
タグの中身です。
このようなViewのブロックを順に並べることで、その順通りに表示されます。
4. EpoxyRecyclerView
にController
をセット
残りはViewにEpoxyRecyclerView
を追加して、作成したController
をセットして完成です。セットにはEpoxyRecyclerView.setController(controller:)
を使って、controller.setData(data:)
でデータを流します。
加えて..
EpoxyVisibilityTracker
スクロールイベントとして、それぞれのRowが表示されているか否か
、どれくらい表示されているか
を引数としたコールバックを設定できます。こちらに詳しいことが記載されています。
val epoxyVisibilityTracker = EpoxyVisibilityTracker() epoxyVisibilityTracker.attach(recyclerView)
Carousel
Epoxy
の利点として、Carousel
が標準で付いていることは大きいです。
導入はとても簡単で、以下のExtensionを追加すれば今までのRowと同様に導入することができます。詳しいことは公式ドキュメントで読んでください。
numViewsToShowOnScreen(num:)
とpadding(padding:)
でアイテムの間隔等を決めます。
padding(padding:)
の引数としてPadding.dp(padding: itemSpacing:)
を渡します。それぞれの変更箇所は図の通りです。現状ではitemSize
を指定してレイアウトを決めるということはできないようです。(2019/2/24)
ピッタっと止めるようにするには、RecyclerView
でも使われているSnapHelperFactory
を作成して、Carousel.setDefaultGlobalSnapHelperFactory(factory:)
を呼び出すことで変更できます。詳しくはこちらで。
注意点として...
CoordinatorLayout
などと一緒に使用する際(Nested Scrollが発生する際)は注意が必要です。
最後に
複雑な画面を作る際に、ScrollView + Fragment
で実装するとパフォーマンスも可読性も落ちてしまう場合があります。
複雑な画面を実装するのに苦労している時は、EpoxyやGroupieなどのRecyclerView
を活用するライブラリを用いて、より簡潔に簡単に作って楽になりましょう!
(次はMvRxについて書こうと思います....)