RecyclerView:检测到不一致。无效的项目位置。原因 - 使用计时器删除项目

Posted

技术标签:

【中文标题】RecyclerView:检测到不一致。无效的项目位置。原因 - 使用计时器删除项目【英文标题】:RecyclerView: Inconsistency detected. Invalid item position. Cause -Removing item using timer 【发布时间】:2018-12-04 17:31:43 【问题描述】:

在继续之前,我在谷歌上搜索了这个错误,我发现了很多可用的答案,但我的情况与他们的情况不同。

java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 2(offset:2).state:3

我正在使用 recyclerview 显示投票问题,每个问题都有一个计时器,当计时器用完时,项目将从列表中删除。

当计时器用完时会发生异常,但仅在极少数情况下剩余时间较少(例如 100 毫秒)。所以在这种情况下,可能是 recyclerview 正在膨胀项目,同时,计时器用完并且 recyclerview 尝试删除该项目。

错误是当计时器删除项目时我得到异常。

因此,当该轮询剩余 1 秒或更少秒时,我从数据集中删除元素后解决了这个问题。所以它不会在列表中添加项目,而是会运行计时器。

如果您想产生错误,只需启动倒数计时器 bindView 并且一旦计时器用完,就删除该特定项目。 您必须使计时器低于 500 毫秒。

所以现在一切正常。解决了罕见情况下的崩溃问题,但即使剩余时间较少,我也不想从数据集中删除该元素。请给我这个错误的正确解决方案。

已编辑

java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 1(offset:1).state:2 com.lsjwzh.widget.recyclerviewpager.RecyclerViewPager182da0c VFED..... .F....ID 0,0-720,1024 #7f0a01a1 app:id/recycler_view, adapter:com.lsjwzh.widget.recyclerviewpager.RecyclerViewPagerAdapter@c9403f, layout:android.support.v7.widget.LinearLayoutManager@6fdbd0c, context:com.bitpoll.polls.MainActivity@b40f4c0
     android.support.v7.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:5817)
     android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5752)
     android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5748)
     android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2232)
     android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1559)
     android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1519)
     android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:614)
     android.support.v7.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:3812)
     android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:3529)
     android.support.v7.widget.RecyclerView.onLayout(RecyclerView.java:4082)
     android.view.View.layout(View.java:17969)
     android.view.ViewGroup.layout(ViewGroup.java:5721)
     android.support.v4.widget.SwipeRefreshLayout.onLayout(SwipeRefreshLayout.java:606)
     android.view.View.layout(View.java:17969)
     android.view.ViewGroup.layout(ViewGroup.java:5721)
     android.widget.RelativeLayout.onLayout(RelativeLayout.java:1189)
     android.view.View.layout(View.java:17969)
     android.view.ViewGroup.layout(ViewGroup.java:5721)
     android.support.constraint.ConstraintLayout.onLayout(ConstraintLayout.java:1855)
     android.view.View.layout(View.java:17969)
     android.view.ViewGroup.layout(ViewGroup.java:5721)
     android.support.design.widget.HeaderScrollingViewBehavior.layoutChild(HeaderScrollingViewBehavior.java:132)
     android.support.design.widget.ViewOffsetBehavior.onLayoutChild(ViewOffsetBehavior.java:42)
     android.support.design.widget.AppBarLayout$ScrollingViewBehavior.onLayoutChild(AppBarLayout.java:1361)
     android.support.design.widget.CoordinatorLayout.onLayout(CoordinatorLayout.java:894)
     android.view.View.layout(View.java:17969)
     android.view.ViewGroup.layout(ViewGroup.java:5721)
     android.widget.RelativeLayout.onLayout(RelativeLayout.java:1189)
     android.view.View.layout(View.java:17969)
     android.view.ViewGroup.layout(ViewGroup.java:5721)
     android.widget.FrameLayout.layoutChildren(FrameLayout.java:383)
     android.widget.FrameLayout.onLayout(FrameLayout.java:321)
     android.view.View.layout(View.java:17969)
     android.view.ViewGroup.layout(ViewGroup.java:5721)
     android.widget.LinearLayout.setChildFrame(LinearLayout.java:1982)
     android.widget.LinearLayout.layoutVertical(LinearLayout.java:1826)
     android.widget.LinearLayout.onLayout(LinearLayout.java:1735)
     android.view.View.layout(View.java:17969)
     android.view.ViewGroup.layout(ViewGroup.java:5721)
     android.widget.FrameLayout.layoutChildren(FrameLayout.java:383)
     android.widget.FrameLayout.onLayout(FrameLayout.java:321)
     android.view.View.layout(View.java:17969)
     android.view.ViewGroup.layout(ViewGroup.java:5721)
     android.widget.LinearLayout.setChildFrame(LinearLayout.java:1982)
     android.widget.LinearLayout.layoutVertical(LinearLayout.java:1826)
     android.widget.LinearLayout.onLayout(LinearLayout.java:1735)
     android.view.View.layout(View.java:17969)
     android.view.ViewGroup.layout(ViewGroup.java:5721)
     android.widget.FrameLayout.layoutChildren(FrameLayout.java:383)
     android.widget.FrameLayout.onLayout(FrameLayout.java:321)
     com.android.internal.policy.DecorView.onLayout(DecorView.java:753)
     android.view.View.layout(View.java:17969)

更新

这是适配器代码。

public class PollAdapter extends RecyclerView.Adapter<PollAdapter.PollViewHolder> 

    // .... 

    @Override
    public void onBindViewHolder(@NonNull PollViewHolder holder, int position) 

    // ...

        if (holder.countDownTimer != null) 
            holder.countDownTimer.cancel();
        

        // CountDown to set Timer in poll
        holder.countDownTimer = new CountDownTimer(<Timer to be finished.>, 1000) 

            public void onTick(long millisUntilFinished) 
                holder.time.setText("<Remaining time>");
            

            public void onFinish() 
                removeAt(holder.getAdapterPosition());
            

        .start();
    

    @Override
    public int getItemCount() 
        return pollList.size();
    

    // To Remove Poll after Time finished
    public void removeAt(int position) 
    pollList.remove(position);
        notifyItemRemoved(position);
    

【问题讨论】:

是否在非 UI 线程上进行任何操作?发布完整的堆栈跟踪可能会有所帮助。 @Cheticamp 添加了完整的堆栈跟踪。 你可以分享你的代码吗? @RahulShukla 没有额外的代码。只需考虑普通的回收站视图适配器。额外的部分是我在onBindView 方法中启动计时器并通过调用notifyItemRemoved(pos) 删除其上的元素onFinished 方法。一切正常,除了,当我添加元素计时器低于 1 秒然后它崩溃了。 @Moinkhan,低于 1 秒的任何内容实际上对用户来说是不可见的。无论如何,您是否为 onBindView() 中的每个项目创建了新的计时器? 【参考方案1】:

RecyclerViewAdapter 未收到更改通知时,会出现此错误。

要解决此错误,请在底层 Adapter 中查找所有数据正在更改的点,没有直接的解决方案,因为这是由 RecyclerView 引发的内部崩溃

你可以做的是:

    既然要添加,请在添加时致电notifyItemInserted 并且由于您也正在删除,因此请在删除时致电 notifyItemRemoved

要在不进行任何优化的情况下进行测试,您也可以同时调用notifyDataSetChanged

【讨论】:

我试过notifyItemRemoved() 仍然会崩溃。但我没用过notifyItemAdded(),我会试试然后回复你。 当然,记住你必须从两个地方调用它,添加和删除,最好是更改。适配器应该总是有最新的数据。它有时会失败,只是因为它在 recyclerview 检查不一致时失败。 没有notifyItemAdded()这样的方法。 notifyItemInserted 键入时未使用 IDE,因此可能会出现名称不匹配的情况。 我尝试了notifyItemRangeInsertednotifyItemInserted,但仍然出现异常。【参考方案2】:

使用自定义LinearLayoutManager

public class CustLinearLayoutManager extends LinearLayoutManager 

public CustLinearLayoutManager(Context context) 
    super(context);


public CustLinearLayoutManager(Context context, int orientation, boolean reverseLayout) 
    super(context, orientation, reverseLayout);


public CustLinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) 
    super(context, attrs, defStyleAttr, defStyleRes);


// Something is happening here

@Override
public boolean supportsPredictiveItemAnimations() 
    return false;
  

用途:-

CustLinearLayoutManager clm = new CustLinearLayoutManagerr(mContext);
recyclerView.setLayoutManager(clm);

我希望它会起作用

【讨论】:

【参考方案3】:

这似乎是同时添加和删除项目的情况。我认为,可以通过按顺序执行所有任务来解决。由于您的代码彼此不相关,我建议您使用recycler.post(new Runnable()); 并将您的代码放在run() 方法中,添加/删除元素,然后notifyDatasetChanged() 应该在此run() 方法中完成。让我知道结果。

【讨论】:

这对我有用。我在后台更新信息,然后在主线程上调用 notifyDataSetChanged()。我可以在后台加载所有信息,然后在主线程上分配信息和更新。【参考方案4】:

@Moinkhan 尝试在 onBindViewHolder 中添加 holder.setIsRecyclable(false)

【讨论】:

【参考方案5】:

我也有类似的问题。我有 5 台设备,但只有一台设备出现错误(lenovo - k6,nougat 7.0)。它在其他牛轧糖手机中运行。这是一个特定于设备的错误

在从活动 A > 活动 B 导航时, 活动 A 的 onDestroy() 被调用。 在 onDestroy() 我正在清除我的静态数组列表。[它只发生在 lenovo - k6, nougat 7.0]

由于我通知适配器为空列表,我在活动 B 中的 recyclerView 上收到错误 OnScrolling。

希望对某人有所帮助。

【讨论】:

【参考方案6】:

问题可能与适配器有关。如果您在适配器构造函数中使用setHasStableIds(true);,则需要确保您使用的是stable ids。

确保在适配器中正确覆盖 getItemId。 应该是:

@Override
public long getItemId(int position) 
   return yourList == null ? 0 : yourList.get(position).getId();

而不是

@Override
public long getItemId(int position) 
     return position;

【讨论】:

【参考方案7】:

这个问题主要是位置问题引起的。

如果您删除一个职位或进行任何更改调用 notifydatasetchanged 那么只会发生更改。

如果你删除一个职位或清除一个数组,如果 notydatasetchanged 没有被调用,recyclerview 将保持职位初始化。

如果有一个数据数组,我刚刚清除了数组并尝试滚动 recyclerview 将显示此问题,因为 notydatasetchanged not called 。 (回收站视图有位置但数组没有找到位置)

注意:对不起我的英语不好

【讨论】:

很抱歉,我已经知道 notifyDatasetChnaged 用于更新 recyclerview。我已经做到了。【参考方案8】:

我有这个错误是因为我在设置 recyclerview 和适配器时忘记删除这行代码:

mRecyclerView.setHasFixedSize(true);

您不能有固定的大小并删除或添加或具有可过滤的回收视图(带搜索)。检查代码中的那一行。

【讨论】:

以上是关于RecyclerView:检测到不一致。无效的项目位置。原因 - 使用计时器删除项目的主要内容,如果未能解决你的问题,请参考以下文章

RecyclerView java.lang.IndexOutOfBoundsException:检测到不一致。视图支架适配器位置无效。由于更改 getItemCount()

RecyclerView 和 java.lang.IndexOutOfBoundsException:检测到不一致。三星设备中的无效视图支架适配器 positionViewHolder

回收商视图:检测到不一致。无效的视图支架适配器 positionViewHolder

RecyclerView:检测到 IndexOutOfBoundsException 不一致。无效的项目位置

检测到不一致的无效视图持有者适配器错误

Gradle:在非 Android 项目上检测到不支持的模块