在平时代码中,最常用的控件之一便是RecyclerView,它的灵活性比ListView高出了很多,google对它的介绍为:
A flexible view for providing a limited window into a large data set.
首先看它的声明:
public class RecyclerView extends ViewGroup implements ScrollingView, NestedScrollingChild2
我们可以看到,RecyclerView其实就是一个ViewGroup,也就是一个View,既然是一个View,那么就离不开那三大绘制流程:onMeasure,onLayout,onDraw·
RecyclerView的onMeasure过程:
onMeasure方法的代码很长,但是总的就分为三种情况:
- 没有LayoutManager的情况
- 有LayoutManager并开启自动测量
- 有LaoutManager但没有开启自动测量
@Override
protected void onMeasure(int widthSpec, int heightSpec) {
if (mLayout == null) {
// 1. 没有LayoutManager的情况
defaultOnMeasure(widthSpec, heightSpec);
return;
}
if (mLayout.mAutoMeasure) {
// 2. 有LayoutManager并开启了自动测量
……
} else {
// 3. 有LayoutManager但没有开启自动测量
……
}
}
首先,我们得知道这个LayoutManager是做什么的:
public abstract static class LayoutManager
它是一个在RecyclerView类中定义的一个内部抽象类,官方提供的实现有LinearLayoutManager
、GridLayoutManager
和StaggeredGridLayoutManager
官方对它的描述:
A
LayoutManager
is responsible for measuring and positioning item views
within aRecyclerView
as well as determining the policy for when to recycle
item views that are no longer visible to the user.
它是用来测量和定位视图的,以及在视图不可见的时候,确定何时回收的策略。
回到onMeasure
方法:
@Override
protected void onMeasure(int widthSpec, int heightSpec) {
if (mLayout == null) {
// 1. 没有LayoutManager的情况
defaultOnMeasure(widthSpec, heightSpec);
return;
}
if (mLayout.mAutoMeasure) {
// 2. 有LayoutManager并开启了自动测量
……
} else {
// 3. 有LayoutManager但没有开启自动测量
……
}
}
void defaultOnMeasure(int widthSpec, int heightSpec) {
// calling LayoutManager here is not pretty but that API is already public and it is better
// than creating another method since this is internal.
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);
}
一种情况很简单,就是执行了defaultOnMeasure
方法,里面就是计算并设置了RecyclerView的长宽值,不处理子VIew了
RecyclerView的自动测量
现在我们常用的三个LayoutManager,在其构造函数中,均已经开启了自动测量,所以我们可以放心地为RecyclerView设置wrap_content。
if (mLayout.mAutoMeasure) {
//得到长宽的mode
final int widthMode = MeasureSpec.getMode(widthSpec);
final int heightMode = MeasureSpec.getMode(heightSpec);
// 检测长宽是否都是精确值
final boolean skipMeasure = widthMode == MeasureSpec.EXACTLY
&& heightMode == MeasureSpec.EXACTLY;
/**mLayout.onMeasure中,就是调用了defaultOnMeasure方法*/
mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
//如果都为精确值,且mAdapter为null,就不需要进入下面的步骤了
if (skipMeasure || mAdapter == null) {
return;
}
//检测RecyclerView的状态,如果是第一次运行,
//mState.mLayoutStep的默认值为State.STEP_START
if (mState.mLayoutStep == State.STEP_START) {
//让适配器更新
//选择使用那个动画
//保存现在视图的信息
//如果有必要的话再进行上一布局操作,并保存它的信息
dispatchLayoutStep1();
}
mLayout.setMeasureSpecs(widthSpec, heightSpec);
mState.mIsMeasuring = true;
//开启布局,计算出所有Child的边界
//在这里会调用Layout.onLayoutChildren方法,这个方法是在子类实现的
dispatchLayoutStep2();
// 布局结束,根据Children中的边界信息计算并设置RecyclerView长宽的测量值
mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
// if RecyclerView has non-exact width and height and if there is at least one child
// which also has non-exact width & height, we have to re-measure.
//如果RecylcerView没有精确的长宽或者至少还有一个的child没有精确的长宽,就需要重新测量
//shouldMeasureTwice()这个方法会在子类中重写,因为父类中直接返回了false
if (mLayout.shouldMeasureTwice()) {
mLayout.setMeasureSpecs(
MeasureS