読者です 読者をやめる 読者になる 読者になる

Got Some \W+ech?

Could be Japanese. Could be English. Android, セキュリティ, 機械学習などをメインに、たまにポエムったり雑感記載したりします。

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をフカボルべき