I'm using RecyclerView
to display name of the items. My row contains single TextView
. Item names are stored in List<String> mItemList
.
To change contents of RecyclerView
, I replace Strings in mItemList
and call notifyDataSetChanged
() on RecyclerViewAdapter
.
But If I try to change contents of the mItemList
while RecyclerView is scrolling, sometimes it gives me
java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 157(offset:157).state:588
This happens if size of mItemList
is less than before. So what is the correct way to change contents of the RecyclerView
? Is this a bug in RecyclerView
?
Here's full stack trace of Exception:
java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 157(offset:157).state:588
at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:3300)
at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:3258)
at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:1803)
at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1302)
at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1265)
at android.support.v7.widget.LinearLayoutManager.scrollBy(LinearLayoutManager.java:1093)
at android.support.v7.widget.LinearLayoutManager.scrollVerticallyBy(LinearLayoutManager.java:956)
at android.support.v7.widget.RecyclerView$ViewFlinger.run(RecyclerView.java:2715)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:725)
at android.view.Choreographer.doCallbacks(Choreographer.java:555)
at android.view.Choreographer.doFrame(Choreographer.java:524)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:711)
at android.os.Handler.handleCallback(Handler.java:615)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4921)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1027)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:794)
at dalvik.system.NativeStart.main(Native Method)
AdapterView code:
private static class FileListAdapter extends RecyclerView.Adapter<FileHolder> {
private final Context mContext;
private final SparseBooleanArray mSelectedArray;
private final List<String> mList;
FileListAdapter(Context context, List<String> list, SparseBooleanArray selectedArray) {
mList = list;
mContext = context;
mSelectedArray = selectedArray;
}
@Override
public FileHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(
R.layout.file_list_item, viewGroup, false);
TextView tv = (TextView) view
.findViewById(R.id.file_name_text);
Typeface font = Typeface.createFromAsset(viewGroup.getContext().getAssets(),
viewGroup.getContext().getString(R.string.roboto_regular));
tv.setTypeface(font);
return new FileHolder(view, tv);
}
@Override
public void onBindViewHolder(FileHolder fileHolder, final int i) {
String name = mList.get(i);
// highlight view if selected
setSelected(fileHolder.itemView, mSelectedArray.get(i));
// Set text
fileHolder.mTextView.setText(name);
}
@Override
public int getItemCount() {
return mList.size();
}
}
private static class FileHolder extends RecyclerView.ViewHolder {
public final TextView mTextView;
public FileHolder(View itemView, TextView tv) {
super(itemView);
mTextView = tv;
}
}
These required methods are as follows: onCreateViewHolder(ViewGroup parent, int viewType) onBindViewHolder(RecyclerView. ViewHolder holder, int position)
If onLayoutChildren is called by RecyclerView, it checks if adapters itemCount is already > 0. If true, it calls scrollToPositionWithOffset() . So I can tell immediately what position should be visible, but it will not be told to LayoutManager before position exists in Adapter. Save this answer.
Edit: The bug is fixed now, if you're still getting the same Exception, please make sure you're updating your Adapter data source only from the main thread and calling appropriate adapter notify method after it.
Old answer: It seems to be a bug in RecyclerView
, it's reported here and here. Hopefully it will be fixed in the next release.
No problem for me. Use NotifyDataSetChanged();
public class MyFragment extends Fragment{
private MyAdapter adapter;
// Your code
public void addArticle(){
ArrayList<Article> list = new ArrayList<Article>();
//Add one article in this list
adapter.addArticleFirst(list); // or adapter.addArticleLast(list);
}
}
public class ArticleAdapterRecycler extends RecyclerView.Adapter<ArticleAdapterRecycler.ViewHolder> {
private ArrayList<Article> Articles = new ArrayList<Article>();
private Context context;
// Some functions from RecyclerView.Adapter<ArticleAdapterRecycler.ViewHolder>
// Add at the top of the list.
public void addArticleFirst(ArrayList<Article> list) {
Articles.addAll(0, list);
notifyDataSetChanged();
}
// Add at the end of the list.
public void addArticleLast(ArrayList<Article> list) {
Articles.addAll(Articles.size(), list);
notifyDataSetChanged();
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With