RecyclerView刷新机制
前面分析了
本文继续来看一下RecyclerView
是如何完成UI的刷新
以及在滑动时子View的添加逻辑
。
本文会从源码分析两件事 :
adapter.notifyXXX()
时RecyclerView的UI刷新的逻辑,即子View
是如何添加到RecyclerView
中的。- 在数据存在的情况下,滑动
RecyclerView
时子View
是如何添加到RecyclerView
并滑动的。
本文不会涉及到RecyclerView
的动画,动画的实现会专门在一篇文章中分析。
adapter.notifyDataSetChanged()引起的刷新
我们假设RecyclerView
在初始状态是没有数据的,然后往数据源中加入数据后,调用adapter.notifyDataSetChanged()
来引起RecyclerView
的刷新:
data.addAll(datas)
adapter.notifyDataSetChanged()
用图描述就是下面两个状态的转换:
adapter.notifyDataSetChanged.png
接下来就来分析这个变化的源码,在上一篇文章中已经解释过,adapter.notifyDataSetChanged()
时,会引起RecyclerView
重新布局(requestLayout
),RecyclerView
的onMeasure
就不看了,核心逻辑不在这里。因此从onLayout()
方法开始看:
RecyclerView.onLayout
这个方法直接调用了dispatchLayout
:
void dispatchLayout() {
...
if (mState.mLayoutStep == State.STEP_START) {
dispatchLayoutStep1();
dispatchLayoutStep2();
} else if (数据变化 || 布局变化) {
dispatchLayoutStep2();
}
dispatchLayoutStep3();
}
上面我裁剪掉了一些代码,可以看到整个布局过程总共分为3步, 下面是这3步对应的方法:
STEP_START -> dispatchLayoutStep1()
STEP_LAYOUT -> dispatchLayoutStep2()
STEP_ANIMATIONS -> dispatchLayoutStep2(), dispatchLayoutStep3()
第一步STEP_START
主要是来存储当前子View
的状态并确定是否要执行动画。这一步就不细看了。 而第3步STEP_ANIMATIONS
是来执行动画的,本文也不分析了,本文主要来看一下第二步STEP_LAYOUT
,即dispatchLayoutStep2()
:
dispatchLayoutStep2()
先来看一下这个方法的大致执行逻辑:
private void dispatchLayoutStep2() {
startInterceptRequestLayout(); //方法执行期间不能重入
...
//设置好初始状态
mState.mItemCount = mAdapter.getItemCount();
mState.mDeletedInvisibleItemCountSincePreviousLayout = 0;
mState.mInPreLayout = false;
mLayout.onLayoutChildren(mRecycler, mState); //调用布局管理器去布局
mState.mStructureChanged = false;
mPendingSavedState = null;
...
mState.mLayoutStep = State.STEP_ANIMATIONS; //接下来执行布局的第三步
stopInterceptRequestLayout(false);
}
这里有一个mState
,它是一个RecyclerView.State
对象。顾名思义它是用来保存RecyclerView
状态的一个对象,主要是用在LayoutManager、Adapter等
组件之间共享RecyclerView状态
的。可以看到这个方法将布局的工作交给了mLayout
。这里它的实例是LinearLayoutManager
,因此接下来看一下LinearLayoutManager.onLayoutChildren()
:
LinearLayoutManager.onLayoutChildren()
这个方法也挺长的,就不展示具体源码了。不过布局逻辑还是很简单的:
- 确定锚点
(Anchor)View
, 设置好AnchorInfo
- 根据
锚点View
确定有多少布局空间mLayoutState.mAvailable
可用 - 根据当前设置的
LinearLayoutManager
的方向开始摆放子View
接下来就从源码来看这三步。
确定锚点View
锚点View
大部分是通过updateAnchorFromChildren
方法确定的,这个方法主要是获取一个View,把它的信息设置到AnchorInfo
中 :
mAnchorInfo.mLayoutFromEnd = mShouldReverseLayout // 即和你是否在 manifest中设置了布局 rtl 有关
private boolean updateAnchorFromChildren(RecyclerView.Recycler recycler, RecyclerView.State state, AnchorInfo anchorInfo) {
...
View referenceChild = anchorInfo.mLayoutFromEnd
? find