回收商视图:检测到不一致。无效的视图支架适配器 positionViewHolder
Posted
技术标签:
【中文标题】回收商视图:检测到不一致。无效的视图支架适配器 positionViewHolder【英文标题】:Recycler View: Inconsistency detected. Invalid view holder adapter positionViewHolder 【发布时间】:2016-06-09 18:29:07 【问题描述】:Recycler View Inconsistency Detected 错误,快速滚动或加载更多项目时滚动..
FATAL EXCEPTION: main
Process: com.pratap.endles-s-recyclerview, PID: 21997
java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter positionViewHolder56a082c position=40 id=-1, oldPos=39, pLpos:39 scrap [attachedScrap] tmpDetached no parent
at android.support.v7.widget.RecyclerView$Recycler.validateViewHolderForOffsetPosition(RecyclerView.java:4251)
at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4382)
at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4363)
at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:1961)
at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1370)
at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1333)
at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:562)
at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:2864)
at android.support.v7.widget.RecyclerView.consumePendingUpdateOperations(RecyclerView.java:1445)
at android.support.v7.widget.RecyclerView.access$400(RecyclerView.java:144)
at android.support.v7.widget.RecyclerView$1.run(RecyclerView.java:282)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:858)
at android.view.Choreographer.doCallbacks(Choreographer.java:670)
at android.view.Choreographer.doFrame(Choreographer.java:603)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:844)
at android.os.Handler.handleCallback(Handler.java:746)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5443)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:728)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
适配器
public class DataAdapter extends RecyclerView.Adapter
private final int VIEW_ITEM = 1;
private final int VIEW_PROG = 0;
private List<Feed> mFeed;
// The minimum amount of items to have below your current scroll position
// before loading more.
private int visibleThreshold = 5;
private int lastVisibleItem, totalItemCount;
private boolean loading;
private OnLoadMoreListener onLoadMoreListener;
public DataAdapter(List<Feed> feeds, RecyclerView recyclerView)
mFeed = feeds;
if (recyclerView.getLayoutManager() instanceof LinearLayoutManager)
final LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView
.getLayoutManager();
recyclerView
.addOnScrollListener(new RecyclerView.OnScrollListener()
@Override
public void onScrolled(RecyclerView recyclerView,
int dx, int dy)
super.onScrolled(recyclerView, dx, dy);
totalItemCount = linearLayoutManager.getItemCount();
lastVisibleItem = linearLayoutManager
.findLastVisibleItemPosition();
if (!loading
&& totalItemCount <= (lastVisibleItem + visibleThreshold))
// End has been reached
// Do something
if (onLoadMoreListener != null)
onLoadMoreListener.onLoadMore();
loading = true;
);
@Override
public int getItemViewType(int position)
return mFeed.get(position) == null ? VIEW_PROG : VIEW_ITEM;
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType)
RecyclerView.ViewHolder vh;
if (viewType == VIEW_ITEM)
View v = LayoutInflater.from(parent.getContext()).inflate(
R.layout.list_row, parent, false);
vh = new StudentViewHolder(v);
else
View v = LayoutInflater.from(parent.getContext()).inflate(
R.layout.progress_item, parent, false);
vh = new ProgressViewHolder(v);
return vh;
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position)
if (holder instanceof StudentViewHolder)
Feed singleStudent= (Feed) mFeed.get(position);
((StudentViewHolder) holder).tvName.setText(singleStudent.getTitle());
((StudentViewHolder) holder).student= singleStudent;
else
ProgressViewHolder.PROGRESS_BAR.setIndeterminate(true);
public void setLoaded()
loading = false;
public void addFeed(Feed feed)
mFeed.add(feed);
//mFeed.addAll(0, (Collection<? extends Feed>) feed);
notifyItemInserted(mFeed.size());
//notifyItemRangeInserted(0,mFeed.size());
notifyDataSetChanged();
//notifyItemInserted(mFeed.size());
//setLoaded();
//notifyItemInserted(mFeed.size());
public void removeAll()
mFeed.clear();
notifyDataSetChanged();
@Override
public int getItemCount()
return mFeed.size();
public void setOnLoadMoreListener(OnLoadMoreListener onLoadMoreListener)
this.onLoadMoreListener = onLoadMoreListener;
public static class StudentViewHolder extends RecyclerView.ViewHolder
public TextView tvName;
public Feed student;
public StudentViewHolder(View v)
super(v);
tvName = (TextView) v.findViewById(R.id.tvName);
//tvEmailId = (TextView) v.findViewById(R.id.tvEmailId);
public static class ProgressViewHolder extends RecyclerView.ViewHolder
//public ProgressBar progressBar;
public static ProgressBar PROGRESS_BAR;
public ProgressViewHolder(View v)
super(v);
PROGRESS_BAR = (ProgressBar) v.findViewById(R.id.progressBar1);
// progressBar = (ProgressBar) v.findViewById(R.id.progressBar1);
活动
public class MainActivity extends AppCompatActivity implements SwipeRefreshLayout.OnRefreshListener
private Toolbar toolbar;
private TextView tvEmptyView;
private RecyclerView mRecyclerView;
private DataAdapter mAdapter;
private LinearLayoutManager mLayoutManager;
private RestManager mManager;
private List<Feed> mFeed;
SwipeRefreshLayout mSwipeRefreshLayout;
protected Handler handler;
private int currentPage=1;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
toolbar = (Toolbar) findViewById(R.id.toolbar);
tvEmptyView = (TextView) findViewById(R.id.empty_view);
mRecyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);
mSwipeRefreshLayout= (SwipeRefreshLayout) findViewById(R.id.swipe_refresh_layout);
mSwipeRefreshLayout.setOnRefreshListener(this);
//studentList = new ArrayList<Student>();
mFeed = new ArrayList<Feed>();
handler = new Handler();
if (toolbar != null)
setSupportActionBar(toolbar);
getSupportActionBar().setTitle("Android Students");
mManager = new RestManager();
// use this setting to improve performance if you know that changes
// in content do not change the layout size of the RecyclerView
mRecyclerView.setHasFixedSize(true);
mLayoutManager = new LinearLayoutManager(this);
// use a linear layout manager
mRecyclerView.setLayoutManager(mLayoutManager);
// create an Object for Adapter
mAdapter = new DataAdapter(mFeed,mRecyclerView);
// set the adapter object to the Recyclerview
mRecyclerView.setAdapter(mAdapter);
// mAdapter.notifyDataSetChanged();
loadData(false);
// if (mFeed.isEmpty())
// mRecyclerView.setVisibility(View.GONE);
// tvEmptyView.setVisibility(View.VISIBLE);
//
// else
// mRecyclerView.setVisibility(View.VISIBLE);
// tvEmptyView.setVisibility(View.GONE);
//
mAdapter.setOnLoadMoreListener(new OnLoadMoreListener()
@Override
public void onLoadMore()
//add null , so the adapter will check view_type and show progress bar at bottom
mFeed.add(null);
mAdapter.notifyItemInserted(mFeed.size() - 1);
handler.postDelayed(new Runnable()
@Override
public void run()
// remove progress item
mFeed.remove(mFeed.size() - 1);
// mAdapter.notifyItemRemoved(mFeed.size());
//add items one by one
int start = mFeed.size();
currentPage++;
Log.d("CurrentPage", String.valueOf(currentPage));
Call<Results> listCall = mManager.getFeedApi().getAllFeeds(1);
listCall.enqueue(new Callback<Results>()
@Override
public void onResponse(Call<Results> call, Response<Results> response)
mSwipeRefreshLayout.setRefreshing(false);
if (response.isSuccess())
if (response.body() != null)
Results feedList = response.body();
// List<Results> newUsers = response.body();
Log.d("Retrofut", String.valueOf(feedList));
for (int i = 0; i < feedList.results.size(); i++)
Feed feed = feedList.results.get(i);
// mFeed.add(feed);
mAdapter.addFeed(feed);
// mAdapter.notifyDataSetChanged();
//mAdapter.notifyItemInserted(mFeed.size());
// mAdapter.notifyDataSetChanged();
@Override
public void onFailure(Call<Results> call, Throwable t)
Log.d("Retrofut", "Error");
mFeed.remove(mFeed.size() - 1);
mAdapter.notifyItemRemoved(mFeed.size());
mAdapter.setLoaded();
mSwipeRefreshLayout.setRefreshing(false);
);
// for (int i = 1; i <= 20; i++)
// studentList.add(new Student("Student " + i, "androidstudent" + i + "@gmail.com"));
//
//
mAdapter.setLoaded();
//or you can add all at once but do not forget to call mAdapter.notifyDataSetChanged();
, 2000);
);
// load initial data
private void loadData(final boolean removePreData)
Call<Results> listCall = mManager.getFeedApi().getAllFeeds(1);
listCall.enqueue(new Callback<Results>()
@Override
public void onResponse(Call<Results> call, Response<Results> response)
if (response.isSuccess())
if (response.body() != null)
// if(removePreData) mAdapter.removeAll();
Results feedList = response.body();
Log.d("Retrofut", String.valueOf(feedList));
for (int i = 0; i < feedList.results.size(); i++)
Feed feed = feedList.results.get(i);
// mFeed.add(feed);
//mAdapter.notifyDataSetChanged();
mAdapter.addFeed(feed);
mSwipeRefreshLayout.setRefreshing(false);
@Override
public void onFailure(Call<Results> call, Throwable t)
Log.d("Retrofut", String.valueOf(t));
mFeed.remove(mFeed.size() - 1);
mAdapter.notifyItemRemoved(mFeed.size());
mAdapter.setLoaded();
mSwipeRefreshLayout.setRefreshing(false);
);
// for (int i = 1; i <= 20; i++)
// studentList.add(new Student("Student " + i, "androidstudent" + i + "@gmail.com"));
//
//
mSwipeRefreshLayout.setRefreshing(true);
@Override
public void onRefresh()
mFeed.clear();
mAdapter.notifyDataSetChanged();
loadData(true);
currentPage=1;
【问题讨论】:
我遇到了同样的错误,因为我使用了无尽的回收器,因为我的数据到达最后一个位置,它没有得到项目计数,就像你设置它在每个滚动中获得 27 个项目计数一样最后可能是你没有得到 27 个项目,所以它会在之后发生,因为我得到了什么问题刚刚创建的逻辑,如果我按项目 mod(%) list.size() 如果它等于 0,我就会得到使用 notifyItemrangeInserted 否则 notifyDataSetChanged(); 有没有办法重现这个?我在我的生产应用程序中遇到了这个崩溃,但无法在本地重现它 如果您在本地生产使用 1GB 内存的低端设备 如果您在对 Room 实体进行更改后收到此错误,而该更改只会影响另一个不是发生此错误的活动,则可能是您重建项目时,它使用了布局代码缓存不适应这些变化,您需要从 Android 系统设置中删除应用中的数据 【参考方案1】:这个问题是 RecyclerView 的一个已知错误。最好的解决办法是,每次刷新 RecyclerView 之前清空列表。
若要解决此问题,只需在更新回收视图之前使用空列表调用 notifyDataSetChanged()。
例如
//Method for refresh recycle view
if (!yourList.isEmpty())
yourList.clear(); //The list for update recycle view
adapter.notifyDataSetChanged();
【讨论】:
我遇到了这个问题 recyclerview LoadMore items @AhamadullahSaikat 你得到答案了吗? 我正在使用 recyclerview、适配器和片段,这对我有用。我只是在我的适配器上添加一个方法来检查我的列表是否为空。boolean isEmpty()return mylist.isEmpty;
然后在我的片段中调用myadapter.isEmpty()
这很奇怪,刷新时清除列表实际上是导致我出现问题的原因。但是我没有调用 notifyDataSetChanged。如果有人要使用此解决方案,请务必确保更新。
有效!经过一小时无用的研究,你拯救了我的一天!【参考方案2】:
它看起来和已知的android bug相似
有相当丑陋,但工作的方法
public class WrapContentLinearLayoutManager extends LinearLayoutManager
//... constructor
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state)
try
super.onLayoutChildren(recycler, state);
catch (IndexOutOfBoundsException e)
Log.e("Error", "IndexOutOfBoundsException in RecyclerView happens");
mRecyclerView.setLayoutManager(new WrapContentGridLayoutManager(getContext(), spanCount));
对我来说,它没有任何副作用。
【讨论】:
应用程序在使用后没有崩溃,但错误仍然存在,这个错误的确切原因是什么? 这是一个非常糟糕的主意,捕获异常并将其静音...issuetracker.google.com/issues/37030377#comment9@Angad Tiwari @payyd spanCount 是什么? 我正在使用最新版本的 RecyclerView: 26.0.2,这个错误有时仍然会出现,所以我想他们没有修复它。 有什么办法可以重现这个崩溃,因为我们在生产中遇到了这个崩溃,但无法在本地重现它【参考方案3】:将此行与设置 recyclerView 一起放置。问题由 将 RecyclerView 的 ItemAnimator 设置为 null。
在科特林中
recyclerView.layoutManager = LinearLayoutManager(this)
recyclerView.itemAnimator = null
在java中
recyclerView.setItemAnimator(null);
【讨论】:
这解决了我的问题,但是如果 itemAnimator 不为空,崩溃的原因是什么 好的,这也解决了我的问题。 这会在添加或删除项目时删除动画 谢谢!现在是 2022 年,您的回答仍然有意义 这是有效的【参考方案4】:使用它来刷新 RecyclerView
items.clear(); //here items is an ArrayList populating the RecyclerView
adapter.notifyDataSetChanged();
items.addAll(list);// add new data
adapter.notifyItemRangeInserted(0, items.size);// notify adapter of new data
`
【讨论】:
谢谢兄弟,这个答案对我来说是 100% 的工作。当我的 recyclerview 在 fragment 内时。非常感谢。【参考方案5】:在我将新项目添加到我的 RV 后,我遇到了类似的问题,而且这个解决方案也帮助了我:
recyclerView.getRecycledViewPool().clear();
adapter.notifyDataSetChanged();
【讨论】:
【参考方案6】:也许你可以在刷新适配器之前试试这个:
dataList.clear();
patrolListAdapter.notifyDataSetChanged();
【讨论】:
【参考方案7】:就我而言,我是以notifyItemInserted(position);
做的
这导致了我这个问题,然后我使用它并且它工作得很好。notifyItemRangeInserted(startIndex,endIndex);
【讨论】:
【参考方案8】:我在无休止/分页RecyclerView
中快速滚动时遇到了这个问题。我的问题的根源在于我在列表的开头有一个“标题”项,这个标题项不是数据源的一部分,它只是插入到adapter
列表的开头。因此,当快速滚动并向RecyclerView
添加新页面项目Adapter
并通知adapter
已插入新数据时,我没有考虑额外的标题项目,从而使适配器列表的大小错误...并导致此异常...
简而言之,如果您在我们的 RecyclerView
适配器中使用页眉/页脚,请确保在更新适配器数据时将其考虑在内。
示例:
public void addNewPageToList(List<MyData> list)
//
// Make sure you account for any header/footer in your list!
//
// Add one to the currentSize to account for the header item.
//
int currentSize = this.adapterList.size() + 1;
this.adapterList.addAll(list);
notifyItemRangeInserted(currentSize, this.adapterList.size());
编辑:
我想你总是可以只使用适配器方法getItemCount()
来获取大小,而不是从“数据列表”中获取大小并添加到它。您的 getItemCount()
方法应该已经考虑到您列表中的任何其他页眉/页脚/等。
【讨论】:
【参考方案9】:问题出在这行代码中:
mFeed = feeds;
您将mFeed
分配给调用者的实例feeds
,因此每当调用者更改它的变量(可能是添加、清除或删除项目)时,您的本地mFeed
就会更改
试着改成
mFeed.addAll(feeds);
不要忘记将mFeed
初始化为任何符合您需求的列表,例如mFeed = new ArrayList<>();
【讨论】:
【参考方案10】:将此行与设置 recyclerView 一起放置。通过将 RecyclerView 的 ItemAnimator 设置为 null 解决了问题。
in kotlin
recyclerView.layoutManager = LinearLayoutManager(this)
recyclerView.itemAnimator = null
【讨论】:
【参考方案11】:我有类似的问题。从 RecyclerView 中删除所有视图对我有帮助:
RecyclerView.LayoutManager layoutManager = mRecyclerView.getLayoutManager();
layoutManager.removeAllViews();
【讨论】:
【参考方案12】:对我来说,问题是当我实施增量搜索时数据集发生变化时,我没有发布 notifyDatasetChanged。
我有一个根据用户在搜索小部件中搜索的内容进行过滤的列表。对于列表中的每个项目,我都在发出一个远程请求,当我得到结果时,我正在更新那个特定的单元格。
我必须同时通知回收站视图才能工作
过滤原始数据集,然后发布数据集更改
this.searchResultTable?.post
this.searchResultTable?.adapter?.notifyDataSetChanged()
收到回复后,再次发布通知
this.searchResultTable?.post
this.searchResultTable?.adapter?.notifyItemChanged(index, updateDataHashMap)
您必须发布更新而不是直接发送通知消息,以防止在视图布局之前更新进入时回收器视图崩溃。
另一个重要的问题是,当您在远程响应后发布个别更新时,您必须确保用户当前看到的列表是发送请求时存在的列表。
【讨论】:
【参考方案13】:对于我在适配器中的情况,有 notifyItemRangeInserted
,我将其替换为 notifyItemRangeChanged
【讨论】:
以上是关于回收商视图:检测到不一致。无效的视图支架适配器 positionViewHolder的主要内容,如果未能解决你的问题,请参考以下文章
RecyclerView 和 java.lang.IndexOutOfBoundsException:检测到不一致。三星设备中的无效视图支架适配器 positionViewHolder