アダプタをRecyclerView.Adapter
に移植しています
私が達成したいこと:ユーザーがデータのフェッチを開始したい終わり近くにスクロールダウンするとき、私は最後にi ProgressBarビューを追加して、より多くのデータが来ていることをユーザーに知らせたいです。
BaseAdapterでこれを実装した方法: on getView
で、最後の近くで要求されたビューで、さらにデータのフェッチを開始し、notifyDataSetChangedを呼び出します(ProgressBarビューを表示するには)そして、その後に必要なビューを返しますgetView
。
RecyclerView.Adapterで試したこと:基本的に同じことを試みましたが、今回はonBindViewHolder
メソッドで行いました。
しかし、このメソッド内でnotifyItemInserted
を呼び出そうとすると、次の例外が発生します。
IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling
試したこと:onBindViewHolder
からonLayoutChildren
がLayoutManager
から呼び出されることに気づいたので、オーバーライドしてnotifyItemInserted
を呼び出してみましたそのsuper
が同じ例外を取得しました
どうすれば目標を達成できますか?
Handler handler = new Handler();
final Runnable r = new Runnable() {
public void run() {
adapter.notifyDataSetChanged();
}
};
handler.post(r);
Adapter.notifyDataSetChanged();を呼び出すサンプルコードアクティビティから
この例外をトリガーするRecyclerView.isComputingLayout()
メソッドのJavaDocに注目する価値もあります。
/**
* Returns whether RecyclerView is currently computing a layout.
* <p>
* If this method returns true, it means that RecyclerView is in a lockdown state and any
* attempt to update adapter contents will result in an exception because adapter contents
* cannot be changed while RecyclerView is trying to compute the layout.
* <p>
* It is very unlikely that your code will be running during this state as it is
* called by the framework when a layout traversal happens or RecyclerView starts to scroll
* in response to system events (touch, accessibility etc).
* <p>
* This case may happen if you have some custom logic to change adapter contents in
* response to a View callback (e.g. focus change callback) which might be triggered during a
* layout calculation. In these cases, you should just postpone the change using a Handler or a
* similar mechanism.
*
* @return <code>true</code> if RecyclerView is currently computing a layout, <code>false</code>
* otherwise
*/
public boolean isComputingLayout() {
return mLayoutOrScrollCounter > 0;
}
顕著なフレーズは:
これらの場合、ハンドラーまたは同様のメカニズムを使用して変更を延期する必要があります。
私の場合、別のスレッドからアダプターの内容を変更していましたが、それが(ときどき)衝突を引き起こしていました。更新をUIスレッドのハンドラーにシフトすると、これは自然に解決されます。
Handler
を使用してアイテムを追加し、このHandler
からnotify...()
を呼び出すと、問題が修正されました。
きれいおよび簡単方法:
recyclerView.post(new Runnable() {
@Override
public void run() {
adapter.notifyDataSetChanged();
}
});
説明:RecyclerView
インスタンスを使用し、postメソッド内で新しいRunnable
をメッセージキューに追加しました。実行可能ファイルは、ユーザーインターフェイススレッドで実行されます。これは、バックグラウンドからUIスレッドにアクセスするためのAndroidの制限です(たとえば、バックグラウンドスレッドで実行されるメソッド内))。
試してみてください
if (!recyclerView.isComputingLayout()) {
recyclerView.notifyDataSetChanged();
}
私はこれを使用します:
Handler handler = new Handler();
private void notifyItemInsertedEx(final int position) {
try {
notifyItemInserted(position);
} catch (Exception ex) {
handler.post(new Runnable(){
public void run(){
notifyItemInserted(position);
}
});
}
}