RecyclerView源码解析

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源码解析)