欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

RecyclerView的bug

程序员文章站 2022-04-26 12:06:06
...

使用 RecyclerView 加SwipeRefreshLayout下拉刷新的时候,adapter绑定的 List 在更新数据之前进行了 clear,而这时用户紧接着迅速上滑 RecyclerView,就会造成崩溃,而且异常不会报到我们的代码上,属于RecyclerView内部错误。
解决办法:
1. 在recyclerView进行刷新在时候,拦截触摸事件,使列表在刷新过程中不能滑动:

 mRecyclerView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if (mRefreshLayout.isRefreshing()) {
                    return true;
                }
                return false;
            }
        });

这个方法虽然管用,但也不能解决所有问题,因为不可避免的,有需要刷新过程中滑动列表的需求,或者其他诡异的操作(我就遇到了..),使刷新的过程中列表仍然滑动了,就还会崩溃。下面在这个方法的基础上,再增加一些措施。

  1. 首先,说明下我遇到的错误:
java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter positionViewHolder{6129a2b position=50 id=-1, oldPos=0, pLpos:0 scrap [attachedScrap] tmpDetached no parent}
    at android.support.v7.widget.RecyclerView$Recycler.validateViewHolderForOffsetPosition(RecyclerView.java:5297)
    at android.support.v7.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:5479)
    at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5440)
    at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:5436)
    at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2224)
    at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1551)
    at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1511)
    at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:595)
    at android.support.v7.widget.RecyclerView.dispatchLayoutStep1(RecyclerView.java:3534)
    at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:3310)
    at android.support.v7.widget.RecyclerView.consumePendingUpdateOperations(RecyclerView.java:1648)
    at android.support.v7.widget.RecyclerView$1.run(RecyclerView.java:343)
    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:860)
    at android.view.Choreographer.doCallbacks(Choreographer.java:672)
    at android.view.Choreographer.doFrame(Choreographer.java:605)
    at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:846)
    at android.os.Handler.handleCallback(Handler.java:739)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:148)
    at android.app.ActivityThread.main(ActivityThread.java:5432)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:735)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:625)

从错误中可以看出,在LinearLayoutManager.onLayoutChildren方法中报错,我们打开这个方法发现它没有处理异常,所以我们可以自定义一个LinearLayoutManager类,并且捕获该异常.

public class MyLinearLayoutManager extends LinearLayoutManager {

    public MyLinearLayoutManager(Context context) {
        super(context);
    }

    public MyLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
        super(context, orientation, reverseLayout);
    }

    public MyLinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    public boolean supportsPredictiveItemAnimations() {
        return false;
    }

    @Override
    public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
        try {
            super.onLayoutChildren(recycler, state);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
并将自定义的LinearLayoutManager添加到recyclerView中:
 mRecyclerView.setLayoutManager(new MyLinearLayoutManager(getContext()));
至此基本可以解决这个问题了。而我这的代码还出现了另一个问题:
java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid item position 72(offset:72).state:84
    at android.support.v7.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:5504)
    at android.support.v7.widget.GapWorker.prefetchPositionWithDeadline(GapWorker.java:282)
    at android.support.v7.widget.GapWorker.flushTaskWithDeadline(GapWorker.java:336)
    at android.support.v7.widget.GapWorker.flushTasksWithDeadline(GapWorker.java:349)
    at android.support.v7.widget.GapWorker.prefetch(GapWorker.java:356)
    at android.support.v7.widget.GapWorker.run(GapWorker.java:387)
    at android.os.Handler.handleCallback(Handler.java:739)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:148)
    at android.app.ActivityThread.main(ActivityThread.java:5432)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:735)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:625)

同样是IndexOutOfBounsException,但并没被上面自定义的LinearLayoutManager捕获。通过走读我的代码,我发现了问题所在:

RecyclerView的bug

在重新增加列表项之前,就已经清空了列表,这时候如果来个诡异的滑动事件,就会导致上面这个问题的出现,所以将代码这样修改:

RecyclerView的bug
OK,这个问题就解决了~

但这个问题,根本原因还是在于RecyclerView内部的错误google并没有处理完善,最好的解决办法还是google来做,但在它出更新之前,我们只能自己处理了~~~