【问题标题】:RecyclerView: Inconsistency detected. Invalid item positionRecyclerView:检测到不一致。无效的项目位置
【发布时间】:2016-12-10 01:39:21
【问题描述】:

我们的 QA 检测到一个错误:在旋转 Android 设备 (Droid Turbo) 时,发生了以下 RecyclerView 相关的崩溃:

java.lang.IndexOutOfBoundsException: 检测到不一致。无效的项目位置 2(offset:2).state:3

对我来说,这看起来像是 RecyclerView 内部的一个内部错误,因为我想不出这是由我们的代码直接引起的...

有人遇到过这个问题吗?

解决办法是什么?

一个残酷的解决方法可能是在异常发生时捕获它并从头开始重新创建 RecyclverView 实例,以避免陷入损坏状态。

但是,如果可能的话,我希望更好地了解问题(并可能从源头解决问题),而不是掩盖它。

这个bug不容易复现,但一旦发生就会致命。

完整的堆栈跟踪:

W/dalvikvm( 7546): threadid=1: thread exiting with uncaught exception (group=0x41987d40)
    E/AndroidRuntime( 7546): FATAL EXCEPTION: main
    E/AndroidRuntime( 7546): Process: com.oblong.mezzedroid, PID: 7546
    E/AndroidRuntime( 7546): java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 2(offset:2).state:3
    E/AndroidRuntime( 7546):    at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:3382)
    E/AndroidRuntime( 7546):    at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:3340)
    E/AndroidRuntime( 7546):    at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:1810)
    E/AndroidRuntime( 7546):    at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1306)
    E/AndroidRuntime( 7546):    at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1269)
    E/AndroidRuntime( 7546):    at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:523)
    E/AndroidRuntime( 7546):    at org.liboid.recycler_view.RecyclerViewContainer$LiLinearLayoutManager.onLayoutChildren(RecyclerViewContainer.java:179)
    E/AndroidRuntime( 7546):    at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:1942)
    E/AndroidRuntime( 7546):    at android.support.v7.widget.RecyclerView.onLayout(RecyclerView.java:2237)
    E/AndroidRuntime( 7546):    at org.liboid.recycler_view.LiRecyclerView.onLayout(LiRecyclerView.java:30)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1671)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1525)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.onLayout(LinearLayout.java:1434)
    E/AndroidRuntime( 7546):    at com.oblong.mezzedroid.workspace.content.bins.BinsContainerLayout.onLayout(BinsContainerLayout.java:22)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1671)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1525)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.onLayout(LinearLayout.java:1434)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1671)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1525)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.onLayout(LinearLayout.java:1434)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1671)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1525)
    E/AndroidRuntime( 7546):    at android.widget.LinearLayout.onLayout(LinearLayout.java:1434)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
    E/AndroidRuntime( 7546):    at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
    E/AndroidRuntime( 7546):    at android.view.View.layout(View.java:14946)
    E/AndroidRuntime( 7546):    at android.view.ViewGroup.layout(ViewGroup.java:4651)
    E/AndroidRuntime( 7546):    at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2132)
    E/AndroidRuntime( 7546):    at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1872)
    E/AndroidRuntime( 7546):    at andro

【问题讨论】:

  • 一个问题:你的再现有多一致?我知道这是谷歌代码here 和here 中的错误。但这可以避免。那么,每次轮换都会发生这种情况吗?
  • 是的,我认为您最好的选择是在轮换期间不允许更改列表视图。
  • 如果您可以轻松复制,我建议您在所有调用 'notify*' 之前打印 'getItemCount' 的值...您可能会发现您的项目数与您的假设不符。
  • 要记住的规则是:“总是在更改数据集后立即通知...(),并确保从同一个线程调用它”。
  • 如果您拨打了adapter.setHasStableIds(),请移除此电话。它对我有用。

标签: android android-layout android-recyclerview indexoutofboundsexception screen-rotation


【解决方案1】:

如果您有多个回收器视图共享同一个适配器,则会弹出此错误,因此为避免此错误,请为这些回收器视图创建单独的适配器。 这使得回收器视图具有非共享池,从而在不需要时阻止适配器填充它们。

【讨论】:

    【解决方案2】:

    RecyclerView 我也遇到了同样的问题 所以我只是在列表清除后立即通知适配器数据集更改。

    mList.clear();
    mAdapter.notifyDataSetChanged();
    
    mList.addAll(newData);
    mAdapter.notifyDataSetChanged();
    

    【讨论】:

      【解决方案3】:

      将 RecyclerView 更新到最新的 1.2.1 版本。 Aaand魔术发生了!不能再破坏我的应用程序了:) 所以..尝试用你的gradle文件中的最新版本替换旧版本号

      dependencies {
          ...
          // RecyclerView
          def recyclerview_version = "1.2.1"
          implementation "androidx.recyclerview:recyclerview:$recyclerview_version"
      }
      

      【讨论】:

        【解决方案4】:

        您可以通过以下方式重现此崩溃

        1. 清除适配器项目list.clear()。 (不要调用 notify*** 方法)
        2. 滚动回收站视图。

        所以我假设当您从列表中删除项目并在不调用通知方法的情况下滚动时会发生这种崩溃

        【讨论】:

          【解决方案5】:

          如果 notifyItemChanged(int position),notifyItemInserted(int position) 互相代替使用 当您要更新项目时使用 notifyItemChanged 以及要添加项目时使用 notifyItemInserted 会出现此问题

          【讨论】:

            【解决方案6】:

            我刚刚解决了同样的问题。我有一个 RecyclerView.Adapter 和 setHasStableIds(true) 设置以避免项目闪烁。

            我在getItemId() 中使用了一个可复制字段(我的模型没有id 字段):

            override fun getItemId(position: Int): Long {
                // Error-prone due to possibly duplicate name.
                return contacts[position].name.hashCode().toLong()
            }
            

            getItemId() 应该为每个项目返回一个唯一的 id,所以解决方案是这样做:

            override fun getItemId(position: Int): Long {
                // Contact's phone is unique, so I use it instead.
                return contacts[position].phone.hashCode().toLong()
            }
            

            【讨论】:

              【解决方案7】:

              如果有人遇到同样的问题,我在我的适配器中重写了这两个方法,效果很好。

                      override fun getItemId(position: Int): Long {
                          return position.toLong()
                      }
                  
                      override fun getItemViewType(position: Int): Int {
                          return position
                      }
              

              【讨论】:

                【解决方案8】:

                扩展 LinearLayoutManager 并捕获此错误

                public class NoCrashLinearLayoutManager extends LinearLayoutManager {
                
                    public NoCrashLinearLayoutManager(Context context) {
                        super(context);
                    }
                
                    public NoCrashLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
                        super(context, orientation, reverseLayout);
                    }
                
                    public NoCrashLinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
                        super(context, attrs, defStyleAttr, defStyleRes);
                    }
                
                    @Override
                    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
                        try {
                            super.onLayoutChildren(recycler, state);
                        } catch (IndexOutOfBoundsException e){
                            e.printStackTrace();
                        }
                    }
                }
                

                【讨论】:

                  【解决方案9】:

                  大多数答案是禁用动画或创建新的数据副本,在我看来这太过分了。他们确实有效,但没有解决问题的根本原因。就我而言,这是由于以错误的方式使用 DiffUtils。在areItemsTheSame(old, new) 方法中,我没有正确实现相等性检查,这反过来又导致notifyItemRangeInserted() 通知适配器,即使新列表中有相同数量的项目。因此,请密切注意如何实现 DiffUtils 回调!

                  【讨论】:

                  • 怎么可能?你是用 Java 编写的(不是带有数据类的 Kotlin)吗?
                  • @CoolMind 我使用的是 Kotlin 数据类,当您在数据类中有另一个对象作为属性时,数据类的相等性检查不会递归调用相等性检查
                  【解决方案10】:

                  在 API 19、21(但不是新的)上引发了此异常。在 Kotlin 协程中,我加载了数据(在后台线程中)并在 UI 线程中添加并显示了它们:

                  adapter.addItem(item)
                  

                  适配器:

                  var list: MutableList<Item> = mutableListOf()
                  
                  init {
                      this.setHasStableIds(true)
                  }
                  
                  open fun addItem(item: Item) {
                      list.add(item)
                      notifyItemInserted(list.lastIndex)
                  }
                  

                  由于某种原因,Android 渲染速度不够快或其他原因,因此,我在 RecyclerView 的 post 方法中更新了一个列表(添加、删除、更新项目事件):

                  view.recycler_view.post { adapter.addItem(item) }
                  

                  此异常类似于“无法在滚动回调中调用此方法。滚动回调可能在您无法更改 RecyclerView 数据的测量和布局传递期间运行。任何可能更改 RecyclerView 结构或适配器内容的方法调用都应该推迟到下一帧。”:Recyclerview - cannot call this method in a scroll callback。

                  【讨论】:

                  • 通过 RxJava 加载数据的问题完全一样。
                  【解决方案11】:

                  在调用adapter.submitList(list)之前使用ListAdapter (androidx.recyclerview.widget.ListAdapter)调用adapter.submitList(null):

                  adapter.submitList(null)
                  adapter.submitList(someDataList)
                  

                  【讨论】:

                    【解决方案12】:

                    就我而言,我遇到了这个异常

                    java.lang.IndexOutOfBoundsException:检测到不一致。无效的视图支架适配器 positionViewHolder

                    以上答案都不适用于我的情况。因为我正在更新/更改适配器中的现有项目,但我使用了

                    myAdapter.notifyItemInserted(position)
                    

                    相反,我应该使用这个

                    myAdapter.notifyItemChanged(position)
                    

                    注意:当我们插入一个项目时我们应该使用notifyItemInserted(),当我们在适配器中更新一个项目时我们应该使用notifyItemChanged()。

                    【讨论】:

                    • 这很奇怪。我也有一次混淆了这两种方法,但并没有导致异常。它有时无法正确更新列表。
                    【解决方案13】:

                    就我而言,这解决了我的问题

                    rv.setAdapter(null);
                    rv.setItemAnimator(null);
                    

                    PS:当我在我的 recyclerview 适配器中进行搜索过滤器时出现问题

                    【讨论】:

                      【解决方案14】:

                      我设法解决此问题的一种方法(在具有架构组件的 Kotlin 应用程序中)是在我从存储库中获取数据后设置 if (recyclerView.adapter == null) recyclerView.adapter = MyAdapter(datasource)。显然,它可能与存储库中挂起函数的异步问题有关,因为当我在启动活动时第一次获取数据时,它会调用 REST API,因为数据库中没有数据,一切都很顺利,但之后,无法再次进行相同的查询,导致崩溃。

                      【讨论】:

                        【解决方案15】:

                        当我没有意识到我确实同时使用不同的线程调用了两次时,问题发生在我身上。

                        notifyDataSetChanged

                        一个来自sqlite加载函数一个来自调用函数后

                        【讨论】:

                          【解决方案16】:

                          回复很晚,但这可能对使用该功能的人有所帮助。

                          确保您的 onStop 或 onPause 方法没有清除您的任何列表

                          【讨论】:

                            【解决方案17】:

                            我遇到了一个(可能)相关的问题 - 使用 RecyclerView 输入活动的新实例,但使用较小的适配器对我来说触发了这个崩溃。

                            RecyclerView.dispatchLayout() 可以在调用mRecycler.clearOldPositions() 之前尝试从废品中提取物品。结果是它从公共池中拉出位置高于适配器大小的项目。

                            幸运的是,它只有在启用PredictiveAnimations 时才会这样做,所以我的解决方案是将GridLayoutManager 子类化(LinearLayoutManager 有同样的问题并“修复”),并覆盖supportsPredictiveItemAnimations() 以返回 false:

                            /**
                             * No Predictive Animations GridLayoutManager
                             */
                            private static class NpaGridLayoutManager extends GridLayoutManager {
                                /**
                                 * Disable predictive animations. There is a bug in RecyclerView which causes views that
                                 * are being reloaded to pull invalid ViewHolders from the internal recycler stack if the
                                 * adapter size has decreased since the ViewHolder was recycled.
                                 */
                                @Override
                                public boolean supportsPredictiveItemAnimations() {
                                    return false;
                                }
                            
                                public NpaGridLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
                                    super(context, attrs, defStyleAttr, defStyleRes);
                                }
                            
                                public NpaGridLayoutManager(Context context, int spanCount) {
                                    super(context, spanCount);
                                }
                            
                                public NpaGridLayoutManager(Context context, int spanCount, int orientation, boolean reverseLayout) {
                                    super(context, spanCount, orientation, reverseLayout);
                                }
                            }
                            

                            【讨论】:

                            • 这对我有用,并且禁用预测动画不会导致您一起丢失动画。太棒了。
                            • 非常感谢先生!立即使用 LinearLayoutManager,可能节省了我的时间。
                            • 非常感谢。此解决方案与 LinearLayoutManager 一起使用。
                            • 我认为这个人值得我们为他的宝贵帮助建造一座雕像...这是网络上记录最差的问题之一,但似乎很多开发人员都遇到了这个问题...我只是想知道如果 PredictiveAnimations 是错误的,@KasHunt,您如何 发现它可以被跳过?因为,堆栈跟踪非常不清楚......
                            • 任何人都知道,没有这个黑客如何解决这个问题?因为 notifyDatasetChanged 被 DiffUtil 取代
                            【解决方案18】:

                            当用户滚动时适配器中的列表清除时会发生此错误,这会使项目持有者的位置发生变化,UI 上列表和项目之间的引用丢失,接下来会发生错误 “notifyDataSetChanged” 请求。

                            修复:

                            查看您的更新列表方法。如果你做类似的事情

                            mainList.clear();
                            ...
                            mainList.add() or mainList.addAll()
                            ...
                            notifyDataSetChanged();
                            
                            ===> Error occur
                            

                            如何解决。为缓冲区处理创建新的列表对象,然后再次分配给主列表

                            List res = new ArrayList();
                            …..
                            res.add();  //add item or modify list
                            ….
                            mainList = res;
                            notifyDataSetChanged();
                            

                            感谢Nhan Cao 的大力帮助:)

                            【讨论】:

                              【解决方案19】:

                              对我来说,添加这行代码后它就起作用了:

                              mRecyclerView.setItemAnimator(null);
                              

                              【讨论】:

                              • 这在大多数情况下不是一个修复,如果你想要动画,你必须重写你的适配器代码并在通知更改时找到你的错误
                              • 它对我来说很好用。我使用的是真正的适配器,所以我无法控制流程,我启用了 windowActivityTransitions 导致这个问题的风格感谢你拯救了我的一天。
                              【解决方案20】:

                              这是唯一对我有用的解决方案,即使从上述解决方案中尝试了很多。

                              1.) 集成

                              CustomAdapter scrollStockAdapter = new CustomAdapter(mActivity, new ArrayList<StockListModel>());
                              list.setAdapter(scrollStockAdapter);
                              scrollStockAdapter.updateList(stockListModels);
                              

                              2.) 在适配器中编写此方法

                              public void updateList(List<StockListModel> list) {
                              stockListModels.clear();
                              stockListModels.addAll(list);
                              notifyDataSetChanged();
                              }
                              

                              stockListModels -> 这个列表是你在适配器中使用的。

                              【讨论】:

                                【解决方案21】:

                                我最近在使用新的 Android 架构组件时遇到了这个讨厌的堆栈跟踪。本质上,我的 ViewModel 中有一个项目列表,我的 Fragment 使用 LiveData 观察到这些项目。当 ViewModel 为数据发布新值时,Fragment 会更新适配器,传入这些新数据元素并通知适配器发生了更改。

                                不幸的是,在将新数据元素传递给适配器时,我没有考虑到 ViewModel 和适配器都指向同一个对象引用的事实!这意味着如果我更新数据并从 ViewModel 中调用 postValue(),则会有一个非常小的窗口可以更新数据并且适配器尚未通知!

                                我的解决方法是在传递给适配器时实例化一个新的元素副本:

                                mList = new ArrayList&lt;&gt;(passedList);

                                通过这个超级简单的修复,您可以确保您的适配器数据在您的适配器收到通知之前不会更改。

                                【讨论】:

                                  【解决方案22】:

                                  对不起,迟到但完美的解决方案: 当您尝试删除特定项目时,只需调用 notifydatasetchange() 并在 bindviewholder 中获取这些项目并删除该项目并再次添加到列表的最后一个,然后检查列表位置,如果这是最后一个索引,然后删除项目。 基本上,当您尝试从中心移除项目时,问题就来了。如果您从最后一个索引中删除项目,则没有更多的回收,并且您的适配器计数是 mantine(这是临界点崩溃来这里)并且崩溃解决了下面的代码 sn-p。

                                   holder.itemLayout.setVisibility( View.GONE );//to hide temprory it show like you have removed item
                                  
                                          Model current = list.get( position );
                                          list.remove( current );
                                          list.add( list.size(), current );//add agine to last index
                                          if(position==list.size()-1){// remove from last index
                                               list.remove( position );
                                          }
                                  

                                  【讨论】:

                                    【解决方案23】:

                                    add_location.removeAllViews();

                                                for (int i=0;i<arrayList.size();i++)
                                                {
                                                    add_location.addView(new HolderDropoff(AddDropOffActivtity.this,add_location,arrayList,AddDropOffActivtity.this,this));
                                                }
                                                add_location.getAdapter().notifyDataSetChanged();
                                    

                                    【讨论】:

                                    • 先移除视图,然后添加占位符视图,然后刷新适配器
                                    【解决方案24】:

                                    在我的例子中,我试图在后台线程上更改我的适配器内容,但在主/ui 线程上调用 notify*。

                                    这是不可能的! 强制通知主线程的原因是 recyclerview 希望您在主线程上编辑您的后备适配器,即使在同一个调用堆栈上。

                                    要解决此问题,请确保对适配器的每个操作以及每个 notify... 调用都在 ui/主线程上进行!

                                    【讨论】:

                                    • 将项目添加到适配器中的列表应该在后台线程上完成,并在执行后调用通知。如果添加许多数据,在 ui 线程中添加数据会使应用程序冻结几毫秒或几秒钟
                                    • 同意@dionellorera,应该明确的是“适配器内容的改变”具体意味着直接修改任何数据,无论是原始值、对象的属性还是对象本身
                                    【解决方案25】:

                                    您只需要在OnPostExecute() 上清除您的列表,而不是在Pull to Refresh 上清除列表

                                    // Setup refresh listener which triggers new data loading
                                            swipeContainer.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
                                                @Override
                                                public void onRefresh() {
                                    
                                                    AsyncTask<String,Void,String> task = new get_listings();
                                                    task.execute(); // clear listing inside onPostExecute
                                    
                                                }
                                            });
                                    

                                    我发现当您在 拉动刷新 期间滚动时会发生这种情况,因为我在 async task 之前清除列表,导致 java.lang.IndexOutOfBoundsException: Inconsistency detected.

                                            swipeContainer.setRefreshing(false);
                                            //TODO : This is very crucial , You need to clear before populating new items 
                                            listings.clear();
                                    

                                    这样你就不会以不一致的方式结束

                                    【讨论】:

                                      【解决方案26】:

                                      在通知之前删除布局管理器的所有视图。喜欢:

                                      myLayoutmanager.removeAllViews();
                                      

                                      【讨论】:

                                      • 它有效。我在滚动加载和标签更改时遇到问题。
                                      【解决方案27】:

                                      Lint 给了我一个关于不一致的建议: 我写了(onBindViewHolder()):

                                      pholder.mRlayout.setOnClickListener(new View.OnClickListener() {
                                                          @Override
                                                          public void onClick(View v) {
                                                              doStuff(position);
                                                          }
                                                      });
                                      

                                      必须替换为:

                                      pholder.mRlayout.setOnClickListener(new View.OnClickListener() {
                                                          @Override
                                                          public void onClick(View v) {
                                                              doStuff(pholder.getAdapterPosition());
                                                          }
                                                      });
                                      

                                      在你的代码中运行这两个代码,然后运行 ​​Lint 以获得完整的解释!!

                                      【讨论】:

                                        【解决方案28】:

                                        就我而言,我刚刚删除了 setHasStableIds(true); 的行

                                        【讨论】:

                                        • 但是 HasStableIds(true) 提高了 Rv 的性能,有没有替代方案?
                                        • 其实我觉得可能是由于不同的原因,所以根据根本原因,这个问题可能会有不同的解决方案。
                                        【解决方案29】:

                                        我也曾经遇到过错误:

                                        原因:我试图从异步任务中更新 Recycler View,同时尝试获取旧的已删除 viewHolders;

                                        代码:我一键生成数据,逻辑如下

                                        1. 清除回收站视图中的最后一项
                                        2. 调用异步任务生成数据
                                        3. OnPostExecute 更新 Recycler 视图和 NotifyDataSetChanged

                                        问题:每当我在生成数据之前快速滚动时,我都会得到

                                        检测到不一致。无效的视图支架适配器 positionViewHolder java.lang.IndexOutOfBoundsException:检测到不一致。无效的项目位置 20(offset:2).state:3

                                        解决方案:我没有在生成我的数据之前清除RecyclerView,而是将其保留,然后用新数据替换它,调用NotifyDatasetChanged,如下图;

                                               @Override
                                                protected void onPostExecute(List<Objects> o) {
                                                    super.onPostExecute(o);
                                                    recyclerViewAdapter.setList(o);
                                                    mProgressBar.setVisibility(View.GONE);
                                                    mRecyclerView.setVisibility(View.VISIBLE);
                                                }
                                        

                                        【讨论】:

                                        【解决方案30】:

                                        若要解决此问题,只需在更新回收视图之前使用空列表调用 notifyDataSetChanged()。

                                        例如

                                        //Method for refresh recycle view
                                        
                                            if (!hcpArray.isEmpty())
                                        

                                        hcpArray.clear();//更新循环视图列表

                                        adapter.notifyDataSetChanged();
                                        

                                        【讨论】:

                                        • 不是解决方案。
                                        • @Milha 我没有找到任何其他解决方案来解决崩溃问题。但上述解决方案对我有用。如果这不是解决方案,请告诉我正确的解决方法。
                                        • 视情况而定。您可以尝试使用 DiffUtil — 一个用于更新 RecyclerView 内容的通用工具。
                                        猜你喜欢
                                        • 2017-09-30
                                        • 2020-01-23
                                        • 2015-08-08
                                        • 2018-12-04
                                        • 2016-07-15
                                        • 1970-01-01
                                        • 1970-01-01
                                        • 1970-01-01
                                        相关资源
                                        最近更新 更多