RecyclerView源码解析

作者: Colaman丶 | 来源:发表于2019-02-20 19:12 被阅读29次
    image.png

    关键性几个方法以及变量

    • dispatchLayoutStep1
    • dispatchLayoutStep2
    • dispatchLayoutStep3
    • ViewInfoStore
    • ViewInfoStore.ProcessCallback

    dispatchLayoutStep1

    第一步负责把旧的viewholder的信息记录下来,包括position,top,left等位置的信息,封装成ItemHolderInfo
    作为一个成员放置到ViewInfoStore.InfoRecord中的preInfo变量然后放到ViewInfoStore中,
    具体可以查看 addToAppearedInPreLayoutHolders/addToPreLayout方法

    dispatchLayoutStep2

    第二步负责view的布局,这一步由layoutmanager来处理,比如设置的LinearlayoutManager

    dispatchLayoutStep3

    第三步负责动画的显示,先是把布局过后的view的信息记录下来,调用了addToPostLayout方法,封装成ItemHolderInfo
    作为一个成员放置到ViewInfoStore.InfoRecord中然后放到ViewInfoStore中,这里的是记录在了InfoRecordpostInfo中去,最后调用this.mViewInfoStore.process(this.mViewInfoProcessCallback); 去开始对view进行动画操作

    这里step1和step3中分别用一个ItemHolderInfo记录了布局前后view的信息,然后放在了InfoRecord中的preInfo以及postInfo中,这样一个InfoRecord就可以记录一个holder在布局改变前后的位置信息,方便后续做动画上的变化

    OnMeasure

    这里的测量分两种方式,recyclerview有默认的测量策略,layoutmanager可以通过关闭autoMeasure来接管测量的逻辑,但是一般的都是使用默认的测量,

     int widthMode = MeasureSpec.getMode(widthSpec);
                    int heightMode = MeasureSpec.getMode(heightSpec);
                    this.mLayout.onMeasure(this.mRecycler, this.mState, widthSpec, heightSpec);
                    boolean measureSpecModeIsExactly = widthMode == 1073741824 && heightMode == 1073741824;
                    // 表明如果recyclerview有一个确切的宽高,就直接结束流程,否则就要开始下面的逻辑
                    if (measureSpecModeIsExactly || this.mAdapter == null) {
                        return;
                    }
                    // 如果recyclerview是wrapcontent,宽高无法确定,就需要先去排列view,计算出高度再去设置
                    // 这里的state记录了当前layout进行到了第几步,详细可以看下面的介绍
                    if (this.mState.mLayoutStep == 1) {
                        this.dispatchLayoutStep1();
                    }
    
                    this.mLayout.setMeasureSpecs(widthSpec, heightSpec);
                    this.mState.mIsMeasuring = true;
                    this.dispatchLayoutStep2();
                    this.mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
                    if (this.mLayout.shouldMeasureTwice()) {
                        this.mLayout.setMeasureSpecs(MeasureSpec.makeMeasureSpec(this.getMeasuredWidth(), 1073741824), MeasureSpec.makeMeasureSpec(this.getMeasuredHeight(), 1073741824));
                        this.mState.mIsMeasuring = true;
                        this.dispatchLayoutStep2();
                        this.mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
                    }
    
    RecyclerView.State 这个类封装了当前RecyclerView的有用信息。State的一个变量mLayoutStep表示了RecyclerView当前的布局状态,包括STEP_STARTSTEP_LAYOUTSTEP_ANIMATIONS三个,而RecyclerView的布局过程也分为三步,其中,STEP_START表示即将开始布局,需要调用dispatchLayoutStep1来执行第一步布局,接下来,布局状态变为STEP_LAYOUT,表示接下来需要调用dispatchLayoutStep2里进行第二步布局,同理,第二步布局后状态变为STEP_ANIMATIONS,需要执行第三步布局dispatchLayoutStep3。所以OnMeasure如果已经进行了第1,2步,后续dispatchLayout方法里只会执行第三步,避免重复测量

    Adapter

    • AdapterDataObserver
    • AdapterDataObservable

    adapter中的数据刷新是通过观察者模式来触发的,adapter内有一个AdapterDataObservable对象,当我们调用adapter的noti类方法通知recyclerview刷新的时候,AdapterDataObservable会通知观察者也就是AdapterDataObserver ,而AdapterDataObserver对象在recyclerview 里有一个实例化对象RecyclerViewDataObserver可以查看内部代码,这里的代码就是最后去刷新的逻辑

    1.调用Adapterhelper中的具体方法去判断是否执行刷新操作,方法内会记录当前操作类型是add/move/change还有itemvcount等信息,一般返回true,如果是notifyDataSetChanged就直接执行到requestLayout 跳到第7
    2.triggerUpdateProcessor
    3. 执行mUpdateChildViewsRunnable这个runnable
    4. consumePendingUpdateOperations
    5. AdapterHelperpreProcess去判断要add还是remove等操作
    6. 调用AdapterHelperpreProcess去回调到recyclerview中的对应的方法,然后调用offsetPositionsForAdd等方法,去确认变换过后的viewhodler的位置信息,比如add remove 之后其他item的坐标等,同时把cachedview也做同样的操作
    7. 调用dispathchLayout方法,走一遍测绘流程,就是一开始说的三步,这个时候新旧viewholder的信息被分别记录起来,最后调用this.mViewInfoStore.process(this.mViewInfoProcessCallback);
    8. 在这里面根据viewholder前后信息,去判断具体每个view是添加删除还是移动等操作,回调到recyclerview里的mViewInfoProcessCallback也就是上面的callback,然后run一个runnable,让recyclerview的ItemAnimator对象调用runPendingAnimations去开始对view进行动画操作,具体可以查看DefaultItemAnimator里的实现

    notifyDataSetChanged

    notifyDataSetChanged会调用processDataSetCompletelyChanged,方法里把mDataSetHasChangedAfterLayout设为了true,后续在step1里processAdapterUpdatesAndSetAnimationFlags判断把mRunSimpleAnimationsmRunPredictiveAnimations射程了false,所以就跳过了记录旧holder这一步,所以调用的时候没有完整的动画

    image.png image.png image.png

    相关文章

      网友评论

        本文标题:RecyclerView源码解析

        本文链接:https://www.haomeiwen.com/subject/rehkyqtx.html