关于Adapter进行数据更新的分析
调用notifyDataSetChanged
这是一个全局更新:
- 他会将所有的
ViewHolder
的flag
设为ViewHolder.FLAG_UPDATE|ViewHolder.FLAG_INVALID
, - 他会将所有的
View.LayoutParams
的mInstesDirty
设置为true
,代表这些数据都脏了. - 将所有的
Recycler.mCachedViews
缓存的ViewHodler
对应的View.LayoutParams
的mInsetsDirty
赋值为true. - 将
mCachedViews缓存的所有
ViewHolder
的flag
赋值为ViewHolder.FLAG_UPDATE|ViewHolder.FLAG_INVALID
- 如果
mAdapter.mHasStableIds
等于false
(默认都是false,除非你自己去改了),还会移除mCachedViews
所有的缓存数据,移除的数据会被添加到RecycledViewPool
里面.
上述所有事情做完会调用requestLayout
重新开始布局.
1 | class Adapter{ |
这里有一点很重要,在执行
notifyDataSetChanged
方法的时候,会去修改成员变量
mState.mStructureChanged
mDispatchItemChangedEvent
mDataSetHasChangedAfterLayout
这几个参数在执行layout
的时候很重要,这几个值也只有在执行方法notifyDataSetChanged的时候才会被赋值为
true
. 当mDataSetHasChangedAfterLayout
为true
的时候,recycleview将不会执行简单动画和预加载动画.
notifyDataSetChanged
和notifyItemChanged
的区别就是:
notifyDataSetChanged
会立马将所有的viewHolder设置为更新状态和无效状态,并且删除了所有mCachedViews
数组里面的数据notifyItemChanged
会设置mAdapterUpdateDuringMeasure
为true.然后在onMeasure
阶段将变化的viewHolder
设置为更新或者移除或者无用等等状态.实现原理就是会将操作保存到mPendingUpdates
数组里面,然后在onMeasure
阶段遍历数组进行对ViewHolder
的flag修改.notifyDataSetChanged
不会有动画效果notifyItemChanged
会有动画效果
在调用全局更新的时候不会用到AdapterHelper.mPendingUpdates
数组.
现在继续看调用notifyDataSetChanged
时触发的requestLayout
.
进入onMeasure
方法:
1 | class Recycleview{ |
关于notifyDatasetChanged
触发的onMeasure
没有什么可以讲的,重点的是在通过notifyItemChanged(int position)
触发的重新测量,这个下面讲,很重要.涉及到了AdapterHelper.mPendingUpdates
数组的使用.
进入onLayout
方法.
1 | class Recycleview{ |
由于这里探究的是notifyDataSetChanged
方法,也就没有继续探究mAdapterHelper.preprocess和mAdapterHepler.consumeUpdatesInOnepass
方法的必要了.因为进去了也是空的数组.为什么空,上面已经分析了.
上面用了很大的篇幅介绍了:processAdapterUpdatesAndSetAnimationFlags()
方法,这个方法很重要:
- 他遍历了数组
mPendingUpdates
也就是我们在执行更新操作notify
时候存入的事件,遍历完的结果就是给对应的ViewHolder
设置对应的Flag
信息,并且这个时候我们也能在LayoutManager
监听到ViewHolder
的变化. - 判断当前的
Layout
需不需要动画.需要动画的话会给mState.mRunSimpleAnimations
和mState.mRunPredictiveAnimations
赋值为true
分析完了dispatchLayout1()
方法,你会发现这个方法就做了一件事情,预布局,全部都是为了动画.如果你没有动画的需求,dispatchLayout1()
直接可以跳过了.并且在调用notifyDataSetChanged
方法的情况下我们的dispatchLayout1()
方法也没干什么事情,因为全局更新不需要动画.
这里探究过头了,dispatchLayout1 displayLayout2 displayLayout3
不是我这里需要探究的.我现在需要探究的是,在调用notifyDataSetChanged
的时候,他将所有的ViewHolder
的flag
值设置为了ViewHolder.FLAG_UPDATE|ViewHolder.FLAG_INVALID
,现在我需要找到他用的地方.
进入LinearLayoutManager
的onLayoutChildren()
1 | class LinearLayoutManager{ |
第一个用到的地方就在detachAndScrapAttachedViews()
.
1 | public void detachAndScrapAttachedViews( Recycler recycler){ |
上面是原代码,下面我进行精简:
1 | void scrapOrRecycleView(){ |
看到了精简的你会发现,只有当不是INVALIDE REMOVE UPDATE
是可以被放进mCachedViews
.否则当是REMOVE INVALID
会被放进mAttachedScrap
里面.如果是UPDATE
会被放进mChangedScrap
调用notifyItemChanged(int position)
这是专门更新某一个ViewHolder
.
这么一个更新操作会被保存在AdapterHelper.mPendingUpdates
数组里面:
1 | boolean onItemRangeChanged(int positionStart, int itemCount, Object payload) { |
构建了操作对象UpdateOp
操作名字是UPDATE
.关于操作有以下四种,对应着不同的更新方式:
- ADD
- REMOV
- UPDATE
- MOVE
这个操作被记录在mPendingUpdates
数组里面.然后设置了Recycleview.mAdapterUpdateDuringMeasure
为true
.关于这个数组什么时候会被用到后面讲.
关于Recycleview动画实现原理
ViewHolder
的状态有以下几种,这也是动画的几种区别:
- PERSISTENT : 针对布局前和布局后都在手机界面上的
View
所做的动画. - REMOVED : 在布局前对用户可见,但是数据已经从数据源中删除.
- ADDED : 新增数据到数据源,并且在布局后对用户可见.
- DISAPPEARING : 数据一直都存在于数据原中,但是布局后从可见变成了不可见.
- APPERAING : 数据一直在数据源中,但是布局后从不可见变成了可见.