前言
抽丝剥茧RecyclerView
系列文章的目的在于帮助Android开发者提高对RecyclerView的认知,本文是整个系列的第二篇。
LayoutManager
是RecyclerView
中的重要一环,使用LayoutManager
就跟玩捏脸蛋的游戏一样,即使好看的五官(好看的子View)都具备了,也不一定能捏出漂亮的脸蛋,好在RecyclerView
为我们提供了默认的模板:LinearLayoutManager
、GridLayoutManager
和StaggeredGridLayoutManager
。
说来惭愧,如果不是看了GridLayoutManager
的源码,我还真不知道GridLayoutManager
竟然可以这么使用,图片来自网络
不过呢,今天我们讲解的源码不是来自GridLayoutManager
,而是线性布局LinearLayoutManager
(GridLayoutManager
也是继承自LinearLayoutManager
),分析完源码,我还将给大家带来实战,完成以下的效果:
时间轴的效果来自TimeLine,自己稍微处理了一下,现在开始进入正题。
目录
一、源码分析
本着认真负责的精神,我把RecyclerView
中用到LayoutManager
的地方大致看了一遍,发现其负责的主要业务:
- 回收和复用子View(当然,这会交给
Recyler
处理)。 - 测量和布局子View。
- 关于滑动的处理。
回收和复用子View
显然不是LayoutManager
实际完成的,不过,子View
的新增和删除都是LayoutManager
通知的,除此以外,滑动处理的本质还是对子View
进行管理,所以,本文要讨论的只有测量和布局子View
的。
测量和布局子View
发生在RecyclerView
三大工作流程,又...又回到了最初的起点?这是我们在上篇讨论过的,如果不涉及到LayoutManager
的知识,我们将一笔带过即可。
1. 自动测量机制
在RecyclerView#onMeasure
方法中,LayoutManager
是否支持自动测量会走不同的流程:
protected void onMeasure(int widthSpec, int heightSpec) {
// ...
if (mLayout.isAutoMeasureEnabled()) {
final int widthMode = MeasureSpec.getMode(widthSpec);
final int heightMode = MeasureSpec.getMode(heightSpec);
// 未复写的情况下默认调用RecyclerView#defaultOnMeasure方法
mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
final Boolean measureSpecModeIsExactly =
widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY;
// 长和宽的MeasureSpec都为EXACTLY的情况下会return
if (measureSpecModeIsExactly || mAdapter == null) {
return;
}
if (mState.mLayoutStep == State.STEP_START) {
dispatchLayoutStep1();
}
// 1. 计算宽度和长度等
mLayout.setMeasureSpecs(widthSpec, heightSpec);
mState.mIsMeasuring = true;
// 2. 布局子View
dispatchLayoutStep2();
// 3. 测量子View的宽和高,并再次测量父布局
mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
if (mLayout.shouldMeasureTwice()) {
// 再走一遍1,2,3
}
} else {
// ...
mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
// ....
}
}
从代码上来看,使用自动测量机制需要具备:
RecyclerView
布局的长和宽的SpecMode
不能是MeasureSpec.EXACTLY
(大概率指的是布局中RecyclerView
长或宽中有WrapContent
)。RecyclerView
设置的LayoutManger
的<