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
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,099评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,473评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,229评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,570评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,427评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,335评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,737评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,392评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,693评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,730评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,512评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,349评论 3 314
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,750评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,017评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,290评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,706评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,904评论 2 335

推荐阅读更多精彩内容