代码之家  ›  专栏  ›  技术社区  ›  Adi

从回收器视图中删除多个(50多个)项目时引发异常(已附加StackTrace)

  •  0
  • Adi  · 技术社区  · 6 年前

    以下是用动画定义的回收器视图:

    mRecyclerView = rootView.findViewById(R.id.list1);
    mRecyclerView.setLayoutManager(new GridLayoutManager(fragmentActivity, 2));
    mRecyclerView.setHasFixedSize(true);
    mAdapter = new MediaGridAdapter(fragmentActivity, mediaModels);
    
    MyScaleInAnimator animator = new MyScaleInAnimator(mAdapter);
    animator.setAddDuration(100);
    animator.setRemoveDuration(100);
    mRecyclerView.setItemAnimator(animator);
    
    

    以下是我如何从回收视图中删除项目:

    public void removeData(String fileType, int position, Context context) {
       mediaModels.remove(position);
       notifyItemRemoved(position);
    }
    

    (当我删除多个项时,此方法被多次迭代调用)。 问题: 当我从Recycler视图中选择少量项目时,上面的Delete方法将按预期工作。但如果我选择了大量项目(50-100)并立即删除,则会引发以下异常:

    E/AndroidRuntime: FATAL EXCEPTION: main
                     Process: com.akl.alldrive, PID: 26558
                     java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter positionViewHolder{f308d5b position=54 id=-1, oldPos=74, pLpos:54 scrap [attachedScrap] tmpDetached no parent} android.support.v7.widget.RecyclerView{7b12cb3 VFED.V... ......I. 0,147-1080,1857 #7f0a0099 app:id/list1}, adapter:jp.wasabeef.recyclerview.adapters.ScaleInAnimationAdapter@780c270, layout:android.support.v7.widget.GridLayoutManager@fb119e9, context:com.akl.alldrive.activities.BaseActivity@2c3cc96
                         at android.support.v7.widget.RecyclerView$Recycler.validateViewHolderForOffsetPosition(RecyclerView.java:5715)
                         at android.support.v7.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:5898)
                         at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5858)
                         at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5854)
                         at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2230)
                         at android.support.v7.widget.GridLayoutManager.layoutChunk(GridLayoutManager.java:557)
                         at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1517)
                         at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:612)
                         at android.support.v7.widget.GridLayoutManager.onLayoutChildren(GridLayoutManager.java:171)
                         at android.support.v7.widget.RecyclerView.dispatchLayoutStep1(RecyclerView.java:3875)
                         at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:3639)
                         at android.support.v7.widget.RecyclerView.consumePendingUpdateOperations(RecyclerView.java:1888)
                         at android.support.v7.widget.RecyclerView$1.run(RecyclerView.java:407)
                         at android.view.Choreographer$CallbackRecord.run(Choreographer.java:966)
                         at android.view.Choreographer.doCallbacks(Choreographer.java:778)
                         at android.view.Choreographer.doFrame(Choreographer.java:710)
                         at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:952)
                         at android.os.Handler.handleCallback(Handler.java:789)
                         at android.os.Handler.dispatchMessage(Handler.java:98)
                         at android.os.Looper.loop(Looper.java:164)
                         at android.app.ActivityThread.main(ActivityThread.java:6798)
                         at java.lang.reflect.Method.invoke(Native Method)
                         at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
                         at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
    E/AndroidRuntime: FATAL EXCEPTION: AsyncTask #4
                     Process: com.akl.alldrive, PID: 26558
                     java.lang.RuntimeException: An error occurred while executing doInBackground()
                         at android.os.AsyncTask$3.done(AsyncTask.java:353)
                         at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:383)
                         at java.util.concurrent.FutureTask.setException(FutureTask.java:252)
                         at java.util.concurrent.FutureTask.run(FutureTask.java:271)
                         at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:245)
                         at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
                         at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
                         at java.lang.Thread.run(Thread.java:764)
                      Caused by: java.lang.IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling android.support.v7.widget.RecyclerView{7b12cb3 VFED.V... ......I. 0,147-1080,1857 #7f0a0099 app:id/list1}, adapter:jp.wasabeef.recyclerview.adapters.ScaleInAnimationAdapter@780c270, layout:android.support.v7.widget.GridLayoutManager@fb119e9, context:com.akl.alldrive.activities.BaseActivity@2c3cc96
                         at android.support.v7.widget.RecyclerView.assertNotInLayoutOrScroll(RecyclerView.java:2880)
                         at android.support.v7.widget.RecyclerView$RecyclerViewDataObserver.onItemRangeRemoved(RecyclerView.java:5308)
                         at android.support.v7.widget.RecyclerView$AdapterDataObservable.notifyItemRangeRemoved(RecyclerView.java:12032)
                         at android.support.v7.widget.RecyclerView$Adapter.notifyItemRemoved(RecyclerView.java:7231)
                         at com.akl.alldrive.adapters.MediaGridAdapter.removeData(MediaGridAdapter.java:60)
    

    如何解决这个问题?

    3 回复  |  直到 6 年前
        1
  •  1
  •   0xA Farid Haq    6 年前

    indexoutofboundsException

    如果超出了所迭代内容的“边界”,则会抛出,要在RecyclerView中修复,需要实现一种机制,以便在每次删除时都将项目位置向上移动,因此它仍将位于“边界”中。

    使用 notifyItemRangeChanged(position, getItemCount());

    来自文档:“在获取此方法内的相关数据项时,应仅使用位置参数,不应保留该参数的副本。如果稍后需要项的位置(例如在单击侦听器中),请使用 RecyclerView.ViewHolder.getAdapterPosition() 它将具有更新的适配器位置。”

        2
  •  0
  •   TheWanderer    6 年前

    不要一次删除一个项目,然后通知删除的每个项目,而是尝试一次全部删除。

    使用某些MediaModel类的示例:

    ArrayList<MediaModel> itemsToRemove = /* however you obtain your list of items to remove */;
    
    medialModel.removeAll(itemsToRemove);
    
    notifyDataSetChanged();
    
        3
  •  0
  •   ישו אוהב אותך Mahavir    6 年前

    这个 java.lang.IndexOutOfBoundsException 发生错误,因为您正试图从适配器中删除一个不存在的项。试图删除非连续项时,不能依赖项索引位置。例如,当您试图在一个大小为10的列表中按顺序删除1、3、8和9索引位置上的项时,将会得到错误信息。 IndexOutOfBoundsException 在位置1和3移除后停止。实际上您没有移除正确的项目,因为当您移除位置1处的项目时,位置3处的前一个项目现在移动到位置2。

    您可以使用 身份证件 作为项目标识符。如果没有ID,请更改数据模型:

    public class MediaModel {
      private int id;
    
      // your other data
    
      // setter and getter
    
    }
    

    然后,在移除项目时,根据ID而不是位置进行检查。像这样:

    public void removeData(String fileType, int id) {
        int position = -1; // no item found
        for (int i = 0; i < mediaModels.size(); i++) {
          MediaModel model = mediaModels.get(i);
          if(model.getId() == id) {
             position = i;
             break; // we have found the item, so stop searching.
          }
    
          if(position >= 0) {
             mediaModels.remove(position);
             notifyItemRemoved(position);
          }
        }
    }