Несоответствие обнаружено в RecyclerView, Как изменить содержимое RecyclerView при прокрутке

Я использую RecyclerView для отображения имени элементов. Моя строка содержит один TextView . Имена элементов хранятся в List<String> mItemList .

Чтобы изменить содержимое RecyclerView , я заменяю строки в mItemList и вызываю notifyDataSetChanged () в RecyclerViewAdapter .

Но если я попытаюсь изменить содержимое mItemList пока прокручивается RecyclerView, иногда он дает мне 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

Это происходит, если размер mItemList меньше, чем раньше. Итак, каков правильный способ изменения содержимого RecyclerView ? Это ошибка в RecyclerView ?

Вот полная трассировка стека 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) 

Код адаптера:

 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; } } 

Изменить: ошибка исправлена, если вы все еще получаете одно и то же исключение, убедитесь, что вы обновляете источник данных адаптера только из основного потока и вызываете соответствующий метод уведомления адаптера после него.

Старый ответ: Кажется, это ошибка в RecyclerView , здесь сообщается здесь и здесь . Надеюсь, это будет исправлено в следующем выпуске.

Для меня не проблема. Используйте 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(); } } 

Просто запретите прокрутку RecyclerView при изменении данных.

Как и мой код:

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

Подробнее о: http://drakeet.me/recyclerview-bug-indexoutofboundsexception-inconsistency-detected-invalid-item-position-solution

Хотя в принятом ответе дается несколько полезных гиперссылок об этой проблеме, неверно, что такое поведение RecyclerView при прокрутке является ошибкой .

Если вы видите это исключение, скорее всего, вы забудете уведомить адаптер после того, как содержимое RecyclerView «изменилось». Люди звонят notifyDataSetChanged() только после того, как элемент добавлен в набор данных. Однако несоответствие происходит не только после того, как вы пополнили адаптер, но также когда вы удалите элемент или очистите набор данных, вы должны обновить представление, уведомив адаптер об этом изменении:

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

В моем случае я попытался очистить адаптер в onStop() и пополнить его в onStart() . Я забыл вызвать notifyDataSetChanged() после очистки адаптера с помощью clear() . Затем, когда я менял состояние с onStop() на onStart() и быстро прокручивал RecyclerView когда набор данных перезагружается, я видел это исключение. Если бы я дождался окончания перезагрузки без прокрутки, исключений не было бы, поскольку на этот раз адаптер можно будет восстановить на плавном уровне.

Короче говоря, RecyclerView , когда он находится в режиме просмотра. Если вы попытаетесь прокрутить представление при изменении данных в наборе данных, вы увидите java.lang.IndexOutOfBoundsException: Inconsistency detected . Чтобы устранить эту проблему, вы должны уведомить адаптер сразу после изменения набора данных.

У меня была аналогичная проблема, но с удалением содержимого. Я также хотел сохранить анимацию. Я закончил использование notifyRemove, а затем передал диапазон. Кажется, это исправляет любые проблемы …

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

Кажется, работает и избавляется от исключения IOB …

Я столкнулся с такой же проблемой, java.lang.IndexOutOfBoundsException: обнаружена несогласованность .

Создайте пользовательский 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; } } 

Создать экземпляр HPLinearLayoutManager .

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

Надеюсь, это поможет вам.

Я получил это для работы с использованием предложения Cocorico в предыдущем ответе ( https://stackoverflow.com/a/26927186/3660638 ), но есть уловка: поскольку я использую SortedList, используя notifyDataSetChanged () каждый раз, когда происходит изменение данных (Добавление, удаление и т. Д.) notifyItemXXXXX(position) вас терять анимации элементов, которые вы получаете с notifyItemXXXXX(position) , поэтому то, что я закончил делать, это использовать ее только тогда, когда я изменяю данные в пакетном режиме, например:

 public void addAll(SortedList<Entity> items) { movieList.beginBatchedUpdates(); for (int i = 0; i < items.size(); i++) { movieList.add(items.get(i)); } movieList.endBatchedUpdates(); notifyDataSetChanged(); } 

Вы должны использовать в своем счете getitem

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

Также обновляя просмотр ресайклера, используйте это

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

Используя это решение, вы не сможете решить проблему, вы просто используете условие внутри onBindViewHolder для разрешения 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); } } 

У меня была такая же проблема, когда я устанавливал новый экземпляр адаптера на основе некоторых критериев выбора.

Я исправил свою проблему, используя RecyclerView.swapAdapter(adapter, true) Когда мы устанавливаем новый адаптер.

Я понимаю, что для меня это исключение возникает, когда происходят две вещи одновременно, т.е.

1) Прокрутка recyclerview

2) изменение набора данных

Итак, я решил эту проблему, отключив прокрутку до вызова notifydatasetchanged.

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

Чтобы отключить прокрутку, я использовал диалог прогресса, чей setCancelable является ложным.

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

Хитрость здесь заключается в том, чтобы включить прокрутку только тогда, когда набор данных был обновлен.

Я воспроизвел эту проблему. Это произошло, когда мы удаляем элементы в фоновом потоке из mList, но не вызываем notifyDataSetChanged (). Теперь, если мы прокручиваем Это исключение отправляется.

Java.lang.IndexOutOfBoundsException: обнаружена несогласованность. Неверное положение позиции 86 (смещение: 86). Состояние: 100

Первоначально у меня было 100 предметов и удалено несколько предметов из фоновой нити.

Кажется, что Recyclerview вызывает getItemCount () для проверки состояния.

Избегайте notifyDatasetHasChanged () и выполните следующие действия:

 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()); } 

Я не вижу ничего неправильного с кодом, который вы опубликовали. Единственное, что мне странно, это эта линия

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

В вашем методе onBindViewHolder в адаптере. Вы также обновляете этот массив, когда вы изменяете размер вашего списка элементов в массиве?

У меня была аналогичная проблема, в то время как я пытаюсь добавить первый элемент в recyclerView с помощью метода notifyItemInserted, поэтому я изменил функцию addItem на моем адаптере, как показано ниже, и он разрешил.

Странная проблема, надеюсь, что скоро это будет исправлено!

 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); } } 

В звуковом коде есть одно предложение:
/ ** * Используется, когда LayoutState сконструирован в состоянии прокрутки. Он должен * установить количество прокрутки, которое мы можем сделать, не создавая новый * вид. Настройки необходимы для эффективной утилизации просмотра. * / Int mScrollingOffset;

В моем случае он решил, изменив mRecyclerView.smoothScrollToPosition(0) на

 mRecyclerView.scrollToPosition(0) 

У меня также была такая же проблема, и я исправил ее, не используя метод notifyItemRangeChanged (). Это хорошо объяснено в

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

У меня такая же проблема с этой проблемой, я очень устал ее искать и решать. Но я нашел ответ на решение, и исключения еще не были выброшены.

 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(); } } } 

Надеюсь, этот ответ решит вашу проблему.

Создать 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; } } 

Попробуйте использовать логический флаг, инициализируйте его как false и внутри метода OnRefresh сделайте это правдой, очистите свой список данных, если флаг равен true, прежде чем добавлять к нему новые данные, а затем сделать его ложным.

Ваш код может быть таким

  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() ; } } 

В моем случае проблема была у меня.

Моя настройка – это механизм Recyclerview, Adapter & Cursor / Loader.

В какой-то момент моего приложения загрузчик уничтожается.

supportLoaderManager.destroyLoader(LOADER_ID_EVENTS)

Я ожидал, что Recyclerview отобразит пустой список, так как я просто удалил их источник данных. Что усложняет поиск ошибок, так это то, что список был виден, а хорошо известное исключение происходило только на fling / scroll / animation.

Это стоило мне нескольких часов. 🙂

Я изменяю данные для RecyclerView в фоновом Thread . Я получил то же Exception что и ОП. Я добавил это после изменения данных:

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

Надеюсь, поможет