关键性几个方法以及变量
- 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
中,这里的是记录在了InfoRecord
的postInfo
中去,最后调用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_START
、STEP_LAYOUT
、 STEP_ANIMATION
S三个,而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. AdapterHelper
的preProcess
去判断要add还是remove等操作
6. 调用AdapterHelper
的preProcess
去回调到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
image.png image.png image.pngnotifyDataSetChanged会调用
processDataSetCompletelyChanged
,方法里把mDataSetHasChangedAfterLayout
设为了true,后续在step1里processAdapterUpdatesAndSetAnimationFlags
判断把mRunSimpleAnimations
和mRunPredictiveAnimations
射程了false
,所以就跳过了记录旧holder
这一步,所以调用的时候没有完整的动画
网友评论