之前学习ListView是为了更好地学习RecyclerView,没想到拖了这么久。
RecyclerView比ListView更加地复杂,主要从两个方面学习:
- RecyclerView 绘制流程
- RecyclerView 缓存复用
- ListView和RecyclerView缓存比较
RecyclerView 涉及的类
- LayoutManager : 负责子View的measure及layout
- Adapter: 适配器、负责创建子View和绑定数据到子View中
- Recycler:ViewHolder的回收和复用,有四种类型的缓存:crapped、cached、exCached、recycled
[1.0] RecyclerView 绘制流程
[1.1] onMeasure 测量
protected void onMeasure(int widthSpec, int heightSpec) {
if (mLayout == null) {
// LayoutManager不存在的情况
// 默认测量,见小节1.1.1
defaultOnMeasure(widthSpec, heightSpec);
return;
}
// 需要自动测量
if (mLayout.mAutoMeasure) {
final int widthMode = MeasureSpec.getMode(widthSpec);
final int heightMode = MeasureSpec.getMode(heightSpec);
// 如果是宽高都是固定数值,skipMeasure为true,跳过测量步骤
final boolean skipMeasure = widthMode == MeasureSpec.EXACTLY
&& heightMode == MeasureSpec.EXACTLY;
mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
// 跳过测量步骤
if (skipMeasure || mAdapter == null) {
return;
}
if (mState.mLayoutStep == State.STEP_START) {
// 收集ItemView的ViewHolder的信息并保存,见小节1.1.2
dispatchLayoutStep1();
}
mLayout.setMeasureSpecs(widthSpec, heightSpec);
// 实际测量
dispatchLayoutStep2();
mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
// 如果recyclerview 有额外的宽高,需要二次测量
if (mLayout.shouldMeasureTwice()) {
// 真正测量,见小节1.1.3
dispatchLayoutStep2();
mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
}
} else {
//若自定义LayoutManager,自行实现
}
}
[1.1.1] defaultOnMeasure
当不存在LayoutManager时候,会继续默认测量
void defaultOnMeasure(int widthSpec, int heightSpec) {
// 根据measureSpec算出一个具体数值
final int width = LayoutManager.chooseSize(widthSpec,
getPaddingLeft() + getPaddingRight(),
ViewCompat.getMinimumWidth(this));
final int height = LayoutManager.chooseSize(heightSpec,
getPaddingTop() + getPaddingBottom(),
ViewCompat.getMinimumHeight(this));
// 设置测量大小
setMeasuredDimension(width, height);
}
// LayoutManager
public static int chooseSize(int spec, int desired, int min) {
final int mode = View.MeasureSpec.getMode(spec);
final int size = View.MeasureSpec.getSize(spec);
switch (mode) {
case View.MeasureSpec.EXACTLY:
return size;
case View.MeasureSpec.AT_MOST:
return Math.min(size, Math.max(desired, min));
case View.MeasureSpec.UNSPECIFIED:
default:
return Math.max(desired, min);
}
}
默认测量,直接是利用View.MeasureSpec测算,所以可能会出现各种问题
[1.1.2] dispatchLayoutStep1
测量的状态有几个:
- STEP_START: 初始状态
- STEP_LAYOUT: 第一阶段preLayout完成,等待第二阶段真正的布局layout
- STEP_ANIMATIONS: 第二阶段layout完毕,等待第三阶段开始处理动画
dispatchLayoutStep1 主要工作:
- 处理adapter的数据更新
- 决定需要运行的动画
- 保存当前views的信息
- 若需要,提前layout和保存信息
private void dispatchLayoutStep1() {
mState.assertLayoutStep(State.STEP_START);
processAdapterUpdatesAndSetAnimationFlags();
if (mState.mRunSimpleAnimations) {
// Step:保存未删除的ViewHolder的信息,进行pre_layout
in