RecyclerView中检测到不一致,如何在滚动时更改RecyclerView的内容
我正在使用RecyclerView
来显示项目的名称。 我的行包含单个TextView
。 项目名称存储在List<String> mItemList
。
要更改RecyclerView
内容,我将replacemItemList
string,并在RecyclerViewAdapter
上调用notifyDataSetChanged
()。
但是,如果我试图改变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
大小比以前小, mItemList
发生这种情况。 那么改变RecyclerView
内容的正确方法是什么? 这是RecyclerView
的错误吗?
这里是例外的完整堆栈跟踪:
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代码:
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; } }
编辑:现在错误已经修复,如果你仍然得到相同的exception,请确保你只是从主线程更新你的适配器数据源,然后调用适当的适配器通知方法。
老答案:这似乎是一个在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
这种行为是错误的 。
如果你看到这个exception,很可能你在RecyclerView
的内容被“改变”之后忘了通知适配器。 只有在项目被添加到数据集之后,人们才会调用notifyDataSetChanged()
。 但是,不仅在您重新填充适配器之后,还会在删除某个项目或清除该数据集时发生不一致,请通过向适配器通知此更改来刷新该视图:
public void refillAdapter(Item item) { adapter.add(item); notifyDataSetChanged(); } public void cleanUpAdapter() { adapter.clear(); notifyDataSetChanged(); /* Important */ }
在我的情况下,我试图清理onStop()
的适配器,并将其重新填充到onStart()
。 在使用clear()
适配器后,我忘记调用notifyDataSetChanged()
clear()
。 然后,每当我将状态从onStop()
更改为onStart()
并在数据集重新加载时迅速滚动RecyclerView
时,我看到了这个exception。 如果我等待重新加载而不滚动,那么这次就不会出现exception,因为适配器可以顺利恢复。
总之, RecyclerView
在视图更改时不一致。 如果在处理数据集中的更改时尝试滚动视图,则会看到java.lang.IndexOutOfBoundsException: Inconsistency detected
。 为了消除这个问题,你应该在数据集被改变后立即通知适配器。
我有一个类似的问题,但删除的内容。 我也想保留animation。 我结束了使用notifyRemove,然后传递范围。 这似乎解决了所有问题…
public void deleteItem(int index) { try{ mDataset.remove(index); notifyItemRemoved(index); notifyItemRangeRemoved(index,1); } catch (IndexOutOfBoundsException e){ notifyDataSetChanged(); e.printStackTrace(); } }
似乎是工作,摆脱IOBexception…
我面临同样的问题, java.lang.IndexOutOfBoundsException:检测到不一致 。
创build自定义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; } }
创buildHPLinearLayoutManager
实例 。
HPLinearLayoutManager hpLinearLayoutManager = new HPLinearLayoutManager(mContext); recyclerView.setLayoutManager(hpLinearLayoutManager);
希望这会帮助你。
问题绝对不是因为回滚查看滚动,而是与notifyDataSetChanged()有关 。 我有一个回收者的观点,我不断地改变数据,即添加和删除数据。 每当我将项目添加到列表中时,我都会调用notifyDataSetChanged() , 但是当项目被删除或列表被清除时, 不会刷新适配器 。
所以要解决:
java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 2(offset:2).state:12 at android.support.v7.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:5456)
我在list.clear()之后调用了adapter.notifyDataSetChanged() ,无论它在哪里都是必需的。
if (!myList.isEmpty()) { myList.clear(); myListAdapter.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(); }
通过使用这个解决scheme,你不能解决这个问题,你只需使用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); } }
我弄清楚,对于我来说,当两件事情同时发生的时候,这个例外就出现了
1)滚动回收站
2)数据集正在改变
所以,我通过禁用滚动来解决这个问题,直到notifydatasetchanged被调用。
leaderAdapter.notifyDataSetChanged(); pDialog.hide();
要禁用滚动,我使用了一个进度对话框,其setCancelable为false。
pDialog = new ProgressDialog(getActivity()); pDialog.setMessage("Please wait..."); pDialog.setCancelable(false);
这里的诀窍是仅在数据集更新后才能进行滚动。
我得到了这个在以前的回答( https://stackoverflow.com/a/26927186/3660638 )中使用Cocoricobuild议的工作,但有一个问题:因为我使用SortedList
,每次使用notifyDataSetChanged()时,数据发生了变化(添加,删除等)使您失去通过notifyItemXXXXX(position)
获得的项目animation,所以我最终做的只是在批量更改数据时才使用它,如:
public void addAll(SortedList<Entity> items) { movieList.beginBatchedUpdates(); for (int i = 0; i < items.size(); i++) { movieList.add(items.get(i)); } movieList.endBatchedUpdates(); notifyDataSetChanged(); }
当我根据一些select标准设置新的适配器实例时,我遇到了同样的问题。
我已经通过使用RecyclerView.swapAdapter(adapter, true)
解决了我的问题当我们设置新的适配器。
我已经复制了这个问题。 当我们从mList中移除后台线程中的项目但不调用notifyDataSetChanged()时发生这种情况。 现在如果我们滚动这个exception正在来临。
java.lang.IndexOutOfBoundsException:检测到不一致。 无效的项目位置86(偏移:86)。状态:100
最初我有100个项目,并从后台线程删除less数项目。
看起来像Recyclerview调用getItemCount()本身来validation状态。
避免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()); }
如果您使用的话,这个问题即将到达recyclerview
adapter.setHasStableIds(true);
如果你这样设置,删除它,并更新适配器内的数据集;
如果您仍然遇到问题,请在获取新数据后使所有视图无效,然后更新数据集。
我没有看到你发布的代码的错误。 唯一奇怪的是这条线
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被构造成滚动状态时使用。 它应该*设置滚动量,我们可以做,而不需要创build一个新的*视图。 这是高效查看回收所需的设置。 * / int mScrollingOffset;
在我的情况下,它通过更改mRecyclerView.smoothScrollToPosition(0)
来解决
mRecyclerView.scrollToPosition(0)
我也有同样的问题,我已经解决了不使用notifyItemRangeChanged()方法。 这是很好的解释
https://code.google.com/p/android/issues/detail?id=77846#c10
我对这个问题也有同样的问题,我很厌倦search和解决它。 但是我已经find答案来解决了,例外情况没有被再次抛出。
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(); } } }
我希望这个答案将解决你的问题。
创buildCustomLinearLayoutManager:
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,如果在将新数据添加到该数据之前为真,则清除dataList,之后使其为false。
你的代码可能是这样的
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,适配器和光标/加载机制。
在我的应用程序的一点,加载程序被销毁。
supportLoaderManager.destroyLoader(LOADER_ID_EVENTS)
我期待Recyclerview将显示一个空列表,因为我刚刚删除了他们的数据源。 是什么让错误发现更复杂的是,该列表是可见的,众所周知的exception只发生在一个fling /滚动/animation。
这花了我几个小时。 🙂
我正在后台Thread
更改RecyclerView
数据。 我得到了和OP一样的Exception
。 我在更改数据后添加了这个:
myRecyclerView.post(new Runnable() { @Override public void run() { myRecyclerAdapter.notifyDataSetChanged(); } });
希望能帮助到你