3

当我尝试填充我的 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所以我得到了漂亮的插入动画

有没有其他人看到这个问题?

编辑2

这是我的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);
        }
4

3 回答 3

7

google 在21.0.2版本中已解决此错误:

compile "com.android.support:support-v4:21.0.2"
compile "com.android.support:recyclerview-v7:21.0.2"
于 2014-11-21T12:10:29.727 回答
2

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

于 2014-11-03T18:47:51.293 回答
1

当您尝试时发生此notifyDataSetChanged/notifyItemRemoved/Added/change崩溃RecyclerView isComputingLayout

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

在这些情况下,您应该使用 Handler 推迟更改

RecyclerView.isComputingLayout()在返回 false之前不要通知更改。

 /**
     * Returns whether RecyclerView is currently computing a layout.
     * <p>
     * If this method returns true, it means that RecyclerView is in a lockdown state and any
     * attempt to update adapter contents will result in an exception because adapter contents
     * cannot be changed while RecyclerView is trying to compute the layout.
     * <p>
     * It is very unlikely that your code will be running during this state as it is
     * called by the framework when a layout traversal happens or RecyclerView starts to scroll
     * in response to system events (touch, accessibility etc).
     * <p>
     * This case may happen if you have some custom logic to change adapter contents in
     * response to a View callback (e.g. focus change callback) which might be triggered during a
     * layout calculation. In these cases, you should just postpone the change using a Handler or a
     * similar mechanism.
     *
     * @return <code>true</code> if RecyclerView is currently computing a layout, <code>false</code>
     *         otherwise
     */
    public boolean isComputingLayout() {
        return mLayoutOrScrollCounter > 0;
    }
于 2017-11-14T12:00:40.230 回答