Mismatch detected in RecyclerView, How to change the contents of RecyclerView when scrolling

I am using RecyclerView to display the name of the elements. My line contains a single TextView . Element names are stored in List<String> mItemList .

To change the contents of the RecyclerView , I replace the rows in the mItemList and call notifyDataSetChanged () on the RecyclerViewAdapter .

But if I try to change the contents of mItemList while the RecyclerView scrolls, sometimes it gives me java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 157(offset:157).state:588 java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 157(offset:157).state:588

This happens if the mItemList smaller than before. So what is the correct way to modify the contents of a RecyclerView ? Is this a bug in RecyclerView ?

Here's the full stack trace of the 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; } } 
+52
android android-recyclerview
Nov 09 '14 at 10:31
source share
25 answers

Edit: Bug fixed, if you still get the same Exception, make sure that you only upgrade the adapter data source from the main thread and call the appropriate adapter notification method after that.

Old answer: It seems that this is a bug in RecyclerView , it reported here and here , I hope it will be fixed in the next release.

+36
Nov 09 '14 at 19:17
source share
— -

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(); } } 
+16
Nov 14 '14 at 9:53
source share

Just prevent RecyclerView from scrolling when changing data.

Like my code:

 mRecyclerView.setOnTouchListener( new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { if (mIsRefreshing) { return true; } else { return false; } } } ); 

More on: http://drakeet.me/recyclerview-bug-indexoutofboundsexception-inconsistency-detected-invalid-item-position-solution

+10
Mar 13 '15 at 8:42
source share

Although the accepted answer provides some useful hyperlinks about this problem, it is not true that this RecyclerView scrolling behavior is an error .

If you see this exception, most likely you will forget to notify the adapter after the contents of the RecyclerView are modified. People call notifyDataSetChanged() only after adding an item to the dataset. However, inconsistency occurs not only after you replenish the adapter, but also when you delete an item or clear the data set, you must update the view by notifying the adapter of this change:

 public void refillAdapter(Item item) { adapter.add(item); notifyDataSetChanged(); } public void cleanUpAdapter() { adapter.clear(); notifyDataSetChanged(); /* Important */ } 

In my case, I tried to clear the adapter in onStop() and replenish it in onStart() . I forgot to call notifyDataSetChanged() after cleaning the adapter with clear() . Then, when I changed the state from onStop() to onStart() and quickly scrolled through the RecyclerView while the dataset was reloading, I saw this exception. If I were waiting for the reboot to finish without scrolling, there would be no exception, as this time the adapter can be restored smoothly.

In short, RecyclerView incompatible when it changes in view mode. If you try to scroll the view while processing changes to the dataset, you will see java.lang.IndexOutOfBoundsException: Inconsistency detected . To fix this problem, you should notify the adapter immediately after changing the data set.

+6
Jul 06 '16 at 17:31
source share

The problem, of course, is not due to recyclerview scrolling, but it is related to notifyDataSetChanged () . I had a recycler view in which I was constantly changing data, i.e. adding and deleting data. I called notifyDataSetChanged () every time I added items to my list, But I did not update the adapter when the item is deleted or the list is deleted.

So, to fix:

 java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 2(offset:2).state:12 at android.support.v7.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:5456) 

I called adapter.notifyDataSetChanged () after list.clear () , wherever it is needed.

 if (!myList.isEmpty()) { myList.clear(); myListAdapter.notifyDataSetChanged(); } 

Since then, I have never encountered this exception. Hope this works the same for others. :)

+5
Aug 31 '17 at 18:16
source share

I had a similar problem, but with content removal. I also wanted to keep the animation. I ended up using notifyRemove and then passed a range. This seems to fix any issues ...

 public void deleteItem(int index) { try{ mDataset.remove(index); notifyItemRemoved(index); notifyItemRangeRemoved(index,1); } catch (IndexOutOfBoundsException e){ notifyDataSetChanged(); e.printStackTrace(); } } 

It seems to be working and getting rid of the IOB exception ...

+3
Jun 17 '15 at 10:35
source share

I ran into the same problem, java.lang.IndexOutOfBoundsException: inconsistency detected .

Create a Custom LinearLayoutManager .

HPLinearLayoutManager.java

 public class HPLinearLayoutManager extends LinearLayoutManager { public HPLinearLayoutManager(Context context) { super(context); } public HPLinearLayoutManager(Context context, int orientation, boolean reverseLayout) { super(context, orientation, reverseLayout); } public HPLinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } /** * Magic here */ @Override public boolean supportsPredictiveItemAnimations() { return false; } } 

Create an instance of HPLinearLayoutManager .

 HPLinearLayoutManager hpLinearLayoutManager = new HPLinearLayoutManager(mContext); recyclerView.setLayoutManager(hpLinearLayoutManager); 

Hope this helps you.

+3
Jul 21 '16 at 9:59
source share

I understand that for me this exception occurs when two things happen simultaneously, i.e.

1) Scrolling recyclerview

2) data set change

So, I solved this problem by disabling scrolling before calling notifydatasetchanged.

 leaderAdapter.notifyDataSetChanged(); pDialog.hide(); 

To disable scrolling, I used the progress dialog, whose setCancelable is false.

 pDialog = new ProgressDialog(getActivity()); pDialog.setMessage("Please wait..."); pDialog.setCancelable(false); 

The trick here is to enable scrolling only when the dataset has been updated.

+3
Aug 26 '16 at 9:18
source share

This problem is suitable for recyclerview if you use

 adapter.setHasStableIds(true); 

if you install so, delete it and update your data set inside the adapter;
if you still encounter a problem, discard all views after receiving new data, and then update your dataset.

+3
Aug 28 '17 at 11:27
source share

I got this to work using Cocorico's suggestion in the previous answer ( https://stackoverflow.com/a/312947/ ), but there is a trick: since I use SortedList , using notifyDataSetChanged () every time a data change occurs (adding, deletion, etc.), you lose the animation of the elements that you get with notifyItemXXXXX(position) , so as a result, I used it only when I change data in batch mode, for example:

 public void addAll(SortedList<Entity> items) { movieList.beginBatchedUpdates(); for (int i = 0; i < items.size(); i++) { movieList.add(items.get(i)); } movieList.endBatchedUpdates(); notifyDataSetChanged(); } 
+2
Jun 16 '15 at 10:35
source share

You should use getitem in your calculation

 public int getItemCount() { if (mList!= null) return mList.size(); else return 0; } 

Also updating the recycler view, use this

 if (recyclerView.getAdapter() == null) { recyclerView.setHasFixedSize(true); mFileListAdapter= new FileListAdapter(this); recyclerView.setAdapter(mFileListAdapter); recyclerView.setItemAnimator(new DefaultItemAnimator()); } else { mFileListAdapter.notifyDataSetChanged(); } 

Using this solution, you will not be able to solve the problem, you just use the condition inside onBindViewHolder to resolve java.lang.IndexOutOfBoundsException

  public void onBindViewHolder(FileHolder fileHolder, final int i) { if(i < mList.size) { String name = mList.get(i); setSelected(fileHolder.itemView, mSelectedArray.get(i)); fileHolder.mTextView.setText(name); } } 
+2
Jun 18 '15 at 9:40
source share

I had the same problem when I installed a new adapter instance based on some selection criteria.

I fixed my problem using RecyclerView.swapAdapter(adapter, true) when we install a new adapter.

+1
Aug 11 '16 at 18:46
source share

I reproduced this problem. This happened when we remove items in the background thread from mList, but do not call notifyDataSetChanged (). Now if we scroll This exception is sent.

java.lang.IndexOutOfBoundsException: inconsistency detected. Invalid position of position 86 (offset: 86) .state: 100

Initially, I had 100 items and removed several items from the background thread.

It seems that Recyclerview is calling getItemCount () to check the status.

+1
Oct 26 '16 at 10:53 on
source share

Avoid notifyDatasetHasChanged () and follow these steps:

 public void setItems(ArrayList<Article> newArticles) { //get the current items int currentSize = articles.size(); //remove the current items articles.clear(); //add all the new items articles.addAll(newArticles); //tell the recycler view that all the old items are gone notifyItemRangeRemoved(0, currentSize); //tell the recycler view how many new items we added notifyItemRangeInserted(0, articles.size()); } 
+1
Nov 11 '16 at 13:21
source share

I do not see anything wrong with the code you posted. The only thing that is strange to me is this line

 setSelected(fileHolder.itemView, mSelectedArray.get(i)); 

in your onBindViewHolder method in the adapter .. do you also update this array when you resize your list of elements in the array?

0
Nov 09 '14 at 12:04 on
source share

I had a similar problem while I try to add the first element to recyclerView using the notifyItemInserted method, so I changed the addItem function on my adapter as shown below and it resolved.

A strange problem, I hope that it will be fixed soon!

 public void addItem(int position, TableItem item) { boolean firstEntry = false; if (items.size() == 0) { firstEntry = true; } items.add(position, item); if (firstEntry) { notifyDataSetChanged(); } else { notifyItemInserted(position); } } 
0
Nov 02 '15 at 7:10
source share

there is one sentence in the sound code:
/ ** * Used when a LayoutState is constructed in a scroll state. Must * set the amount of scroll that we can do without creating a new * View. Settings are necessary for efficient viewing utilization. * / int mScrollingOffset;

0
Nov 26 '15 at 16:45
source share

In my case, it was resolved by changing mRecyclerView.smoothScrollToPosition(0) to

 mRecyclerView.scrollToPosition(0) 
0
Jun 05 '16 at 10:38 on
source share

I also had the same issue and fixed it without using the notifyItemRangeChanged () method. This is well explained in

https://code.google.com/p/android/issues/detail?id=77846#c10

0
Aug 15 '16 at 8:50
source share

I have the same problem with this problem, I am very tired of looking for and solving it. But I found the answer to the solution, and the exceptions were not thrown again.

 public class MyLinearLayoutManager extends LinearLayoutManager { public MyLinearLayoutManager(Context context) { super(context); } public MyLinearLayoutManager(Context context, int orientation, boolean reverseLayout) { super(context, orientation, reverseLayout); } public MyLinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } @Override public boolean supportsPredictiveItemAnimations() { return false; } @Override public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { //override this method and implement code as below try { super.onLayoutChildren(recycler, state); } catch (Exception e) { e.printStackTrace(); } } } 

I hope this answer solves your problem.

0
Aug 16 '16 at 2:13
source share

create CustomLinearLayoutManager:

 public class CustomLinearLayoutManager extends LinearLayoutManager { public CustomLinearLayoutManager(Context context) { super(context); } public CustomLinearLayoutManager(Context context, int orientation, boolean reverseLayout) { super(context, orientation, reverseLayout); } public CustomLinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } @Override public boolean supportsPredictiveItemAnimations() { return false; } public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { try { super.onLayoutChildren(recycler, state); } catch (IndexOutOfBoundsException e) { e.printStackTrace(); } } @Override public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) { try { return super.scrollVerticallyBy(dy, recycler, state); } catch (Exception e) { e.printStackTrace(); } return 0; } } 
0
Aug 24 '16 at 5:25
source share

try using a logical flag, initialize it as false, and inside the OnRefresh method make it true, clear your data list if the flag is true, before adding new data to it and then make it false.

your code might look like this

  private boolean pullToRefreshFlag = false ; private ArrayList<your object> dataList ; private Adapter adapter ; public class myClass extend Fragment implements SwipeRefreshLayout.OnRefreshListener{ private void requestUpdateList() { if (pullToRefresh) { dataList.clear pullToRefreshFlag = false; } dataList.addAll(your data); adapter.notifyDataSetChanged; @Override OnRefresh() { PullToRefreshFlag = true reqUpdateList() ; } } 
0
Oct 19 '16 at 17:42 on
source share

In my case, I had a problem.

My setup is the Recycliewiew, Adapter, and Cursor / Loader mechanism.

At some point in my application, the bootloader is destroyed.

supportLoaderManager.destroyLoader(LOADER_ID_EVENTS)

I expected Recyclerview to display an empty list since I just deleted their data source. What complicates the search for errors is that the list was visible, and the well-known exception occurred only when scrolling / scrolling / animation.

It cost me a few hours. :)

0
Apr 04 '17 at 7:41 on
source share

I am changing data for RecyclerView in the background Thread . I got the same Exception as the OP. I added this after changing the data:

myRecyclerView.post(new Runnable() { @Override public void run() { myRecyclerAdapter.notifyDataSetChanged(); } });

Hope this helps

0
May 04 '17 at 10:46 a.m.
source share

do this when you want to add a view (e.g. notifyData or addView or something like that)

 if(isAdded()){ // // add view like this. // // celebrityActionAdapter.notifyItemRangeInserted(pageSize, 10); // // } 
0
Jan 19 '19 at 13:24
source share



All Articles