Edited at

OnScrollListener#onScrolled()のコールバックでエラーになる

More than 1 year has passed since last update.


はじめに

RecyclerViewを使って何らかのデータをリスト表示する時にリストの最後尾を表示したタイミングで新しくデータをロードしたいことがあります。そういった場合RecyclerViewにOnScrollListenerをセットしてコールバック処理を実装します。このコールバック処理内で RecyclerView.Adapter#notifyDataSetChanged をすると次のような警告がでます。

Cannot call this method in a scroll callback. Scroll callbacks mightbe run during a measure & layout pass where you cannot change theRecyclerView data. Any method call that might change the structureof the RecyclerView or the adapter contents should be postponed tothe next frame.

java.lang.IllegalStateException:
at android.support.v7.widget.RecyclerView.assertNotInLayoutOrScroll(RecyclerView.java:2625)
at android.support.v7.widget.RecyclerView$RecyclerViewDataObserver.onChanged(RecyclerView.java:4974)
at android.support.v7.widget.RecyclerView$AdapterDataObservable.notifyChanged(RecyclerView.java:11423)
at android.support.v7.widget.RecyclerView$Adapter.notifyDataSetChanged(RecyclerView.java:6680)
...


警告がでる例

レイアウトは次のようなイメージ。


activity_main.xml

<LinearLayout

android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</LinearLayout>

実装は次のようなイメージ。


MainActivity.java

private SomeAdapter adapter;

public void initializeRecyclerView() {
RecyclerView recyclerView = getActivity().findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager());
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
// loading...
adapter.notifyDataSetChanged();
}
});
}



対策

コールバック部分を抜粋する。コールバック内で直接 notifyDataSetChanged をするのではなく一旦postすることで回避できます。


MainActivity.java

@Override

public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
// loading...
recyclerView.post(new Runnable() {
@Override
public void run() {
adapter.notifyDataSetChanged();
}
});
}


参考