はじめに
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();
}
});
}