groupie の差分更新でうまい感じにアニメーションさせる
groupie が便利というよりも、素の RecyclerView が不便すぎるというべきだと思いますけれど、楽ですよね。
groupie はテキストベースのリファレンスは充実していないので、まずはサンプルコードをちゃんと読みこむ必要があります。そうすれば、特に実装に困ることはないと思います。
差分更新をちゃんと機能させる方法について、公式サンプルの UpdatableItem.kt
を読めばおおよそ事足りるのですが、解説を書き残してみます。
モデルクラスをちゃんと作る
まず、モデルクラスは純粋なデータのみを持った data class とすることが望ましいです。
すくなくとも、コンテンツの中身が書き換わったということを検知するためには、モデルオブジェクトの同値性が正しく実装されている必要があります。data class を用いれば自動的に実現されます。
このとき、モデルクラスは自身を識別する一意なキーを持っていると望ましいです。
data val Model(val id: Int, val text: String)
getId() を override した BindableItem を実装する
Item#getId()
を override して、同一のモデルであることを一意なキーで示します。
hashCode()
と equals()
はモデルクラスの同値性と同じになるようにします。BindableItem
自体を data class にすれば、これも自動的に実現できます。
data class MyItem(val model: Model) : BindableItem<ItemModelBinding>() { override fun getLayout() = R.layout.list_item_model override fun bind(viewBinding: ItemModelBinding, position: Int) { viewBinding.model = model } override fun getId(): Long = model.id.toLong() }
これで、追加・更新・削除のすべてのパターンで適切にアニメーションされるようになります。
パターン | 結果 |
---|---|
アイテムが追加された | 既存リストに同一の ID がないので追加と判定される |
アイテムが更新された | ID は同一だが、同値ではないので更新と判定される |
アイテムが削除された | 新規リストに同一の ID がないので削除と判定される |
付加的なオブジェクトを渡したい場合
BindableItem
にモデル以外のオブジェクトを渡す必要があり、data class の備える同値性が壊れてしまう場合、data class をやめて、以下のように equals()
と hashCode()
を明示的に実装します。
override fun equals(other: Any?): Boolean = model.equals(other) override fun hashCode(): Int = model.hashCode()