以下是用动画定义的回收者视图:
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);
}
(当我删除多个项目时,此方法称为多次迭代)。 问题: 当我从“回收者”视图中选择少量项目时,上述删除方法将按预期运行。但是,如果我选择大量项目(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)
如何解决这个问题?
答案 0 :(得分:1)
如果您用尽了迭代的“界限”,则会抛出IndexOutOfBoundsException
。要在recyclerview中修复,您需要实现一种机制,无论何时删除,都将项目位置向上移动,因此它仍将位于“界限”内。
使用notifyItemRangeChanged(position, getItemCount());
摘自文档:“在获取此方法内的相关数据项时,您仅应使用position参数,而不应保留其副本。如果以后需要某个项目的位置(例如,在点击监听器中),使用RecyclerView.ViewHolder.getAdapterPosition()
,它将具有更新的适配器位置。”
答案 1 :(得分:0)
与其一次删除一个项目,然后通知已删除的每个项目,不如一次尝试全部完成。
使用某些MediaModel类的示例:
ArrayList<MediaModel> itemsToRemove = /* however you obtain your list of items to remove */;
medialModel.removeAll(itemsToRemove);
notifyDataSetChanged();
答案 2 :(得分:0)
发生java.lang.IndexOutOfBoundsException
错误是因为您正在尝试从适配器中删除不存在的项目。尝试删除非连续项目时,您不能依赖于项目索引位置。例如,当您尝试顺序删除大小为10的列表中的1、3、8和9索引位置上的项目时,将显示错误IndexOutOfBoundsException
,并且在删除位置1和3后停止。而且您实际上并没有删除正确的项目,因为在位置1删除项目后,位置3的上一个项目现在移到了位置2。
您可以使用 id 作为商品标识符来解决此问题。如果没有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);
}
}
}
答案 3 :(得分:0)
代码中的问题是您尝试通过位置访问mediaModels中的数据。在某些情况下,这将导致索引超出范围,因为调用mediaModels.remove(position)
时会从中删除元素该位置并调整/移动其他元素,例如,如果我在mediaModels中有5个元素:汉堡包,三明治,比萨饼,冰淇淋,热狗...,并且我将mediaModels.remove(position)位置设为1,则删除三明治并转移比萨饼,冰淇淋,热狗填补了空的位置,所以现在mediaModels的大小现在是4,不再是5。如果我随后在位置为4的地方调用mediaModels.remove(position)
,则由于IndexOutOfBoundsException
的大小mediaModels列表已更改。
因此,您需要做的是在循环中将要删除的元素与mediaModels中的元素进行比较,以找到所需的确切位置,然后将该位置值放入:
mediaModels.remove(i);
notifyItemRemoved(i);
我将方法参数位置替换为targetElement,并假设它是一个String,仅用于说明,因此您的代码应如下所示:
public void removeData(String fileType, String targetElement, Context context)
{
for(int i=0;i<mediaModels.size();++i) {
if(mediaModels.get(i).equals(targetElement){//we have found the position
mediaModels.remove(i);
notifyItemRemoved(i);
break;
}
}
}