关于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.mStructureChangedmDispatchItemChangedEventmDataSetHasChangedAfterLayout
这几个参数在执行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 : 数据一直在数据源中,但是布局后从不可见变成了可见.