填充RecyclerView时ArrayIndexOutOfBoundsException

时间:2014-10-30 20:20:12

标签: android android-recyclerview

当我尝试填充我的RecyclerView时,我经常收到错误,但错误似乎发生在StaggeredGridLayoutManager内部。

我从我的数据库填充RecyclerView,然后使用此

将图像添加到我的适配器
List<WImage> images;

public void addImages(LinkedHashMap<Integer,WImage> newImages){
    for(Map.Entry<Integer,WImage> entry : newImages.entrySet()){
        addImage(entry.getValue(),entry.getKey());
    }
}

public void addImage(WImage image, int position){
    images.add(position,image);
    notifyItemRangeInserted(position, 1);

}

奇怪的是,当应用程序首次加载时它很好并且正确显示所有内容但是如果我使用基于数据库查询的不同图像集替换当前在RecyclerView中的内容,那么再次返回初始图像我得到这个错误。

java.lang.ArrayIndexOutOfBoundsException: src.length=16 srcPos=0 dst.length=0 dstPos=0 length=16
            at java.lang.System.arraycopy(Native Method)
            at android.support.v7.widget.StaggeredGridLayoutManager$LazySpanLookup.ensureSize(StaggeredGridLayoutManager.java:2277)
            at android.support.v7.widget.StaggeredGridLayoutManager$LazySpanLookup.offsetForAddition(StaggeredGridLayoutManager.java:2323)
            at android.support.v7.widget.StaggeredGridLayoutManager.handleUpdate(StaggeredGridLayoutManager.java:1280)
            at android.support.v7.widget.StaggeredGridLayoutManager.onItemsAdded(StaggeredGridLayoutManager.java:1253)
            at android.support.v7.widget.RecyclerView$5.dispatchUpdate(RecyclerView.java:406)
            at android.support.v7.widget.RecyclerView$5.onDispatchSecondPass(RecyclerView.java:422)
            at android.support.v7.widget.AdapterHelper.consumePostponedUpdates(AdapterHelper.java:119)
            at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:1896)
            at android.support.v7.widget.RecyclerView.resumeRequestLayout(RecyclerView.java:1101)
            at android.support.v7.widget.RecyclerView$1.run(RecyclerView.java:155)
            at android.view.Choreographer$CallbackRecord.run(Choreographer.java:761)
            at android.view.Choreographer.doCallbacks(Choreographer.java:574)
            at android.view.Choreographer.doFrame(Choreographer.java:543)
            at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:747)
            at android.os.Handler.handleCallback(Handler.java:733)
            at android.os.Handler.dispatchMessage(Handler.java:95)
            at android.os.Looper.loop(Looper.java:136)
            at android.app.ActivityThread.main(ActivityThread.java:5086)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:515)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601)
            at dalvik.system.NativeStart.main(Native Method)

它可以很好地通过所有图像但是当它准备再次呼叫onBindViewHolder时它会崩溃。

当我更改RecyclerView中的数据时,我首先删除所有内容,然后填充查询中的新数据

public void removeAll(){
    images.clear();
    notifyDataSetChanged();
}

我还尝试一次插入所有内容,然后使用起始位置和图像总数调用notifyItemRangeInserted,但它仍然会因同样的错误而崩溃。

修改

当我遇到大约需要放入网格的大约20个项目时,似乎会发生这种情况。如果我只是使用notifyDatasetChanged它似乎工作正常,但我想使用notifyItemRangeInserted所以我得到了很好的插入动画

还有其他人看过这个问题吗?

EDIT2

这是我的onCreateViewHolder

@Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, final int i) {

    View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.image_normal_grid_cell, viewGroup, false);
    return new ViewHolder(v);
}

和我的onBindViewHolder

@Override
public void onBindViewHolder(final ViewHolder holder, final int i) {
    final WImage image = images.get(i);
    holder.v.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            listener.onItemClick(v,i,image);
        }
    });

    holder.heart.setActivated(image.getLikeDislike());

    holder.heart.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Uri.Builder ub = SyncProvider.CONTENT_ID_URI_BASE.buildUpon();
            ub.appendPath(String.valueOf(image.getId()));
            ContentValues values = new ContentValues();

            if(image.getLikeDislike()){
                values.put(SyncProvider.LIKE,0);
                holder.heart.setActivated(false);
                image.setLikeDislike(false);
            }else{
                values.put(SyncProvider.LIKE,1);
                holder.heart.setActivated(true);
                image.setLikeDislike(true);
            }
            values.put(SyncProvider.IMAGE_CHANGED,1);
            context.getContentResolver().update(ub.build(),values,null,null);
        }
    });
    if(cancelPotentialDownload(image.getUri(),holder.imageview)){
        LoadImageTask task = new LoadImageTask(holder.imageview,image.getThumbUri());
        holder.imageview.setImageDrawable(new AsyncBitmap(task));
        task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
    }
}

编辑3

这是我更换适配器时所做的事情

关于导航抽屉的选定索引更改我初始化我的加载器进行查询

public void initLoader(int page){
    if(currentPage != page && mAdapter != null){
        mAdapter.removeAll();
        hashImages.clear();
        getLoaderManager().destroyLoader(currentPage);
    }
    currentPage = page;
    getLoaderManager().initLoader(page,null,this);
}

然后在我的onLoadFinished我的加载器中我将对象添加到适配器

@Override
public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) {
    if(cursor != null && cursor.moveToFirst()){
            do{
                WImage image = new WImage(cursor);
                if(!hashImages.containsKey(image.getId())){
                    newImages.put(tempHash.size(),image);
                }else if(image.getImageChanged()){
                    update = true;
                }
                int pos = tempHash.size();
                tempHash.put(image.getId(),pos);
            }while(cursor.moveToNext());
        }

    if(newImages.size() > 0){
            mAdapter.addImages(newImages);
        }

3 个答案:

答案 0 :(得分:7)

谷歌在 21.0.2

版本中已经解决了这个问题
compile "com.android.support:support-v4:21.0.2"
compile "com.android.support:recyclerview-v7:21.0.2"

答案 1 :(得分:2)

我认为这个问题是由https://code.google.com/p/android/issues/detail?id=77846触发的。 您是否可以使用特定的通知事件(notifyDataSetChanged)替换notifyItemRemoved/Added次来电并查看是否可以重现它?

答案 2 :(得分:0)

当您在notifyDataSetChanged/notifyItemRemoved/Added/change

时尝试RecyclerView isComputingLayout时,会发生此崩溃
  

如果您有一些自定义逻辑来更改适配器,则可能会发生这种情况   响应View回调的内容

     

在这些情况下,您应该使用处理程序推迟更改

请勿在{{1​​}}返回false之前通知更改

RecyclerView.isComputingLayout()