Epoxyを使い始めて半年くらいたちまして、色々Epoxyの使い方わかってきたのでTipsとしてまとめます。
ヘッダー・フッターを表示したい
TypedEpoxyController
の場合
buildModels
で表示するアイテムの前後に入れてあげればそれがヘッダーフッターとして表示されます。
例えば、ヘッダーとして表示したいアイテムがItemHeaderModel_
、フッターとして表示したいアイテムがItemFooterModel_
としたこう書きます。
override fun buildModels(data: Items) { ItemHeaderModel_() .id("header") .addTo(this) // 表示したいアイテムの処理 ItemFooterModel_() .id("footer") .addTo(this)
PagedListEpoxyController
の場合
addModels
をoverride
してsuper.addModels(newModel)
の前後に書きます。
※buildItemModel
じゃないところが微妙なハマりポイントです。
例えば、ヘッダーとして表示したいアイテムがItemHeaderModel_
、フッターとして表示したいアイテムがItemFooterModel_
としたこう書きます。
override fun addModels(models: List<EpoxyModel<*>>) { ItemHeaderModel_() .id("header") .addTo(this) super.addModels(newModel) ItemFooterModel_() .id("footer") .addTo(this)
アイテムにLiveDataを適応したい (ex.ヘッダーに表示件数を表示したい)
ViewModel
に持たせているLiveData
の状態によりアイテムを変更させたい場合、まず、DataBinding
でViewModel
を渡します。
次にinterface
を介してLifecyclerOwner
をセットします。
例えば、ヘッダーのアイテムに表示件数を表示したい場合、Controller
Fragment
ViewModel
XML
はそれぞれこのような感じになります。
Controller
class MyController(val callback: Callback, val viewModel: MyViewModel) : TypedEpoxyController<List<Item>>() { interface Callback { fun headerBind(view: DataBindingEpoxyModel.DataBindingHolder) } override fun buildModels(data: List<Item>) { ItemHeaderModel_() .viewModel(viewModel) .onBind { _, view, _ -> callback.headerBind(view) } .id("header") .addTo(this) }
Fragment
class MyFragment : Fragment(), MyController.Callback { private lateinit var viewModel: MyViewModel private val controller by lazy { MyController(this, viewModel) } override fun headerBind(view: DataBindingEpoxyModel.DataBindingHolder) { view.dataBinding.setLifecycleOwner(this) // interfaceにしてlifecycleOwnerにFragementをセットします。 }
ViewModel
class MyViewModel : ViewModel() { val count: MutableLiveData<Int> = MutableLiveData()
XML
<layout> <data> <variable name="viewModel" type="com.sample.package.MyViewModel" /> </data> <ConstraintLayout ... > <TextView text="@{viewModel.count}" ....
アイテムにお気に入り機能をつけたい
アイテムにお気に入り機能があり、クリックでオンオフのViewを変えたいとして、Viewだけを操作してもダメです。 Epoxyにアイテムが変わったことを通知させないといけません。
この場合EpoxyModel
をカスタマイズします。
例えば、フラグでお気に入りのオンオフを管理するアイテムをFavoriteItem
として、カスタマイズするEpoxyModel
をFavoriteEpoxyModel
として書くとしたらこうなります。
XML(item_favorite.xml)
<layout> <data> <variable name="item" type="com.sample.package.FavoriteItem" /> <variable name="favoriteClickListener" type="android.view.View.OnClickListener" /> </data> ... <ImageView ... android:tint="@{item.isFavorite ? @color/FavoriteOn : @color/FavoriteOff}" android:onClick="@{favoriteClickListener}" ...
FavoriteItem
data class FavoriteItem( val id: Int var isFavorite: Boolean = false ...
FavoriteEpoxyModel
@EpoxyModelClass(layout = R.layout.item_favorite) abstract class FavoriteEpoxyModel : DataBindingEpoxyModel() { @EpoxyAttribute lateinit var item: FavoriteItem @EpoxyAttribute(EpoxyAttribute.Option.DoNotHash) var favoriteClickListener: View.OnClickListener? = null override fun setDataBindingVariables(binding: ViewDataBinding?) { binding?.setVariable(BR.item, item) binding?.setVariable(BR.favoriteClickListener, favoriteClickListener) } }
FavoriteController
FavoriteEpoxyModel_() .item(item) // FavoriteItem .favoriteClickListener { model, parentView, clickedView, position -> callback.onFavoriteClickListener(position) } .id(item.id)
Fragment
override fun onFavoriteClickListener(position: Int) { // listの中から該当するアイテム見つけてお気に入りをオン // リスト更新 controller.setData(newList) }
ページングアイテムの途中に別のViewを挿入する
PagedListEpoxyController
で表示するアイテムの途中に別のViewを挿入したい場合、リストそのものを操作します。
どうやるのかなとEpoxyのissueを見ていたらこちらを発見し、 https://github.com/airbnb/epoxy/issues/563#issuecomment-428319365 「Epoxy側でどうこうするよりKotlinでList操作するのが良いよ」ってことらしいです。
例えば、5つ目に別のアイテムを表示したい場合こうなります。
PagedListEpoxyController
override fun addModels(models: List<EpoxyModel<*>>) { // 挿入したい別のアイテム val otherModel = OtherModel_() .id("other") val newModel = models.toMutableList() when (newModel.size < 5) { true -> newModel.add(otherModel) // アイテム数が5未満の場合は最後に追加 false -> newModel.add(5, otherModel) // アイテム数が5以上の場合は5番目に追加 } super.addModels(newModel) ...
以上です。