RecyclerView源码分析之onMeasure,onLayout,onDraw

在平时代码中,最常用的控件之一便是RecyclerView,它的灵活性比ListView高出了很多,google对它的介绍为: A flexible view for providing a limited window into a large data set.最近做项目需要重写它,现在就来分析分析它。首先看它的声明:public class RecyclerView...
摘要由CSDN通过智能技术生成

在平时代码中,最常用的控件之一便是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方法的代码很长,但是总的就分为三种情况:

  1. 没有LayoutManager的情况
  2. 有LayoutManager并开启自动测量
  3. 有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类中定义的一个内部抽象类,官方提供的实现有LinearLayoutManagerGridLayoutManagerStaggeredGridLayoutManager

官方对它的描述:

A LayoutManager is responsible for measuring and positioning item views
within a RecyclerView 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
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值