RecyclerView.Adapterのnotify系メソッド 〜 notifyDataSetChangedで…いいの?


12月1日は「データセンターの日」とのことで……

今日は何の日によると、12月1日は「データセンターの日」という記念日だそうな。Data CenterとDeCemberが雰囲気似ているという理由から、ソフトバンクIDC(現IDCフロンティア)が制定したんだとか。

というわけで、「データ」にまつわるAndroid

CafeSnapというアプリでは、Design Support Libraryを使用して、マテリアルデザインを導入しつつあります。マテリアルデザインの設計思想では、これまでのOSより自由で、魅力的なモーションを用いたUIが、比較的簡単に実装できるようになりました。

今日は、マテリアル化をすすめる中で遭遇した、RecyclerViewに渡すデータの「更新」に関するメソッドを、まとめてみることにします。

そもそもRecyclerViewって…

RecyclerViewは、しばしばGridViewやListViewなどの、データ一覧を表示するViewと比較されますが、RecyclerViewは少し違います。GridViewやListViewが、データを表示することに特化していると思いますが、RecyclerViewはリストのアイテムを操作する方に寄っているように思います。

notifyDataSetChangedばかり使っていないか

とりあえずadapterに渡しているobjectにaddしたりaddAllしたら、とりあえずnotifyDataSetChangedしてませんか。そもそもnotifyDataSetChangedは、リスト全体を更新するためのメソッド。ページングのためのメソッドではない。他に何かあるの。

RecyclerView.Adapterのnotify系メソッド一覧

ListViewやGridViewなどありますが、今回はRecyclerViewのnotify系メソッドをまとめます。
今後のことも考えて、RecyclerViewを使えるようになるのは大事な気がしたので。

RecyclerView.Adapter | Android Developers
こちらをなんとなく翻訳してみます。

notifyDataSetChanged()

final void notifyDataSetChanged()

データセットが変更されたことを、登録されているすべてのobserverに通知する。

RecyclerViewに表示されているItem全体に更新をかけるようです。Item内でアニメーション(例えばフェードインとか)を使用しているViewがあると、すべてのItemのアニメーションが実行されます。

notifyItemChanged(int position)

final void notifyItemChanged(int position)

指定したpositionのitemが変更されたことを、登録されているすべてのobserverに通知する。

adapterにindexOf()とかメソッドを作っておくと、実装しやすくなったりします。

Adapterの実装例
List<MyModel> _objects;

public int indexOf(MyModel object) {
    return _objects.indexOf(object);
}
ActivityやFragment側の実装例
// 初期化済みとする
MyRecyclerViewAdapter _adapter;

// 仮に、Itemが変化したことを通知するコールバックがあったとして……
public void onItemChanged(MyModel object) {
    int position = _adapter.indexOf(object);
    _adapter.notifyItemChanged(position);
}

OnClickとかのリスナは、結局Adapterにつけるしかないのですかね、なんだか変な感じがしますが……。

notifyItemChanged(int position, Object payload)

final void notifyItemChanged(int position, Object payload)

これはまだ使った経験のないメソッドですが、どうやら……
positionのitemをpayloadで渡したオブジェクトで交換してくれる、というものなのだと思います。
普段は、自分でaddAllみたいなメソッドを実装しなくても、Object渡したら交換できちゃうってことなのだと思います。

payloadで渡したUIオブジェクトの部分を更新できると言うもののようです。
詳しい実装方法は、こちらの記事に書いてありました!↓
RecyclerView の notifyItemChanged をもっと便利に使う

notifyItemInserted(int position)

final void notifyItemInserted(int position)

指定したpositionに新しいitemが挿入されたことを、登録されているすべてのobserverに通知する。

notifyItemMoved(int fromPosition, int toPosition)

final void notifyItemMoved(int fromPosition, int toPosition)

fromPositionのitemが、toPositionへ移動したことを、登録されているすべてのobserverに通知する。

notifyItemRangeChanged(int positionStart, int itemCount)

final void notifyItemRangeChanged(int positionStart, int itemCount)

positionStartの位置からitemCountの範囲において、データの変更があったことを登録されているすべてのobserverに通知する。

notifyItemRangeChanged(int positionStart, int itemCount, Object payload)

final void notifyItemRangeChanged(int positionStart, int itemCount, Object payload)

positionStartの位置からitemCountの範囲において、データの変更があったことを登録されているすべてのobserverに通知する。

このメソッドも使ったことないけど、payloadに入れたオブジェクトで更新してもらえるんだと思います。
違うのかな……???

payloadで渡したUIオブジェクトの部分を更新できると言うもののようです。
詳しい実装方法は、こちらの記事に書いてありました!↓
RecyclerView の notifyItemChanged をもっと便利に使う

notifyItemRangeInserted(int positionStart, int itemCount)

final void notifyItemRangeInserted(int positionStart, int itemCount)

positionStartの位置からitemCountの範囲において、データの変更があったことを登録されているすべてのobserverに通知する。

notifyItemRangeRemoved(int positionStart, int itemCount)

final void notifyItemRangeRemoved(int positionStart, int itemCount)

positionStartを先頭にitemCountの範囲のitemがデータセットから削除されたことを、登録されているすべてのobserverに通知する。

notifyItemRemoved(int position)

final void notifyItemRemoved(int position)

指定したpositionに存在したitemがデータセットから削除されたことを、登録されたすべてのobserverに通知する。

このメソッドを呼ぶと、消えた行を埋めるようにアニメーションされてかっこいい!

こんな感じで。
みなさんのRecyclerView、RecyclerView.Adapter実装の参考になればと思います。