RecyclerViewAdapterのnotifyDataSetChangedとnotifyItemRangeChangedの違い
- notifyDataSetChangedでリスト(recyclerView)を更新したい時に、意図しない挙動になってた
- このリスト内の値がかわることはあるけども、構成自体は固定だっった。
- このリストはnestedScrollViewの中にある
- 例えばあるモデルを選択して、そのモデルをリストにbindしnotifyDataSetChangedをすると、一瞬だけ順番が入れ替わった後に戻るような挙動
- つまりリスト内のモデルをまるっと更新したときに"ちらつき"が発生していた
- 最初はynzmさんのRecyclerView の notifyItemChanged() 時のちらつきを止めるかと思ったが、解決できず
- notifyItemRangeChangedにしたら、"ちらつき”がなくなった
- 直感的だが、notifyDataSetChangedは下記の通りViewをrelayoutをするので、そこに差分があるように思えた。ので中身をみてみた。
private class RecyclerViewDataObserver extends AdapterDataObserver { @Override public void onChanged() { // notifyDataSetChanged内部で呼び出される assertNotInLayoutOrScroll(null); if (mAdapter.hasStableIds()) { // TODO Determine what actually changed. // This is more important to implement now since this callback will disable all // animations because we cannot rely on positions. mState.mStructureChanged = true; setDataSetChangedAfterLayout(); } else { mState.mStructureChanged = true; setDataSetChangedAfterLayout(); } if (!mAdapterHelper.hasPendingUpdates()) { requestLayout(); } } @Override public void onItemRangeInserted(int positionStart, int itemCount) { assertNotInLayoutOrScroll(null); if (mAdapterHelper.onItemRangeInserted(positionStart, itemCount)) { triggerUpdateProcessor(); } } }
- このrequestLayout内部で、ViewのrequestLayoutを呼んでいる
- ViewのrequestLayoutはリファレンスにもある通り、レイアウト構成が変わったときにView階層を再計算させるために呼び出すべきものらしい
Call this when something has changed which has invalidated the layout of this view. This will schedule a layout pass of the view tree.
- よって構成が固定されているリストのアイテムの更新において、notifyDasetChangedの使用は望ましくないので、notifyItemRangeChangedを使うべし。
- ただ今までrecyclerViewの更新でnotifyDasetChangedを使用してたこともあったが、今回のようなことはおきなかった。これはnestedScrollViewの中にrecyclerViewを入れていることが関係するかもしれない(再描画が走ってしまいnestedScrollViewの高さが変化してた?)
- 深く調べるならnestedScrollView及びrecyclerViewをフカボルべき