深入代码层面理解Android下View绘制流程(一)——Measure

这篇博客主要从源代码的角度和大家深入探讨一下Android下View的绘制流程中的Measure过程,
Android下View绘制流程整体上分为三个步骤,Measure(测量),Layout(布局),Draw(绘制),具体来说,当Application启动之后,首先启动一个默认的Activity,接着,在对这个Activity进行绘制时,会从根视图(ViewRoot)的performTraversals()方法开始,从上到下遍历整个视图树,每个View 负责绘制自己,而ViewGroup还要负责通知自己的子View进行绘制操作,performTraversals()方法核心代码如下:

private void performTravelsals(){
    '''
    int childWidthMeasureSpec = getRootMeasureSpec(mWidth,lp.width);
    int childWidthMeasureSpec = getRootMeasureSpec(mWidth,lp.width);
    ...
    //进行测量
    performMeasure(childWidthMeasureSpec,childHeightMeasureSpec);
    ...
    //进行布局
    performLayout(lp,desireWindowWidth,desireWindowHeight);
    ...
    //进行绘制
    performDraw();
    ...
}

下面具体来看

  1. 测量,从上面performTravelsals()可以看出,测量方法第一步是去调用performMeasure()方法,这个方法的代码如下
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec){
    ...
    mView.measure(childWidthMeasureSpec,childHeightMeasureSpec);
    ...
}

具体的测量时分发给ViewGroup(这里的mView就是一个ViewGroup)进行的,这里ViewGroup先调用measure方法对其自身进行测量,接着会调用measureChildren()方法对其子View进行遍历并对每个子View调用measure()方法实现子View的测量,以下为measureChildren()方法的核心代码:

private void measureChildren(int widthMeasureSpec, int heightMeasureSpec){
    final int size = mChildrenCount;
    final View[] children = mChildren;
    for(int i = 0; i < size; ++i){
        final View child = children[i];
        //当View的可见性处于GONE状态时,不对其进行测量
        if((child.mViewFlags & VISIBILITY_MASK) != GONE){
            measureChild(child, widthMeasureSpec, heightMeasureSpec);
        }
    }
}

接下来是measureChild()方法的核心代码:

private void measureChild(View child, int parentWidthMeasureSpec, int parentHeigthMeasureSpec){
    final LayoutParams lp = child.getLayoutParams();
    //根据父容器的MeasureSpec(即传过来的第二个和第三个参数)和子View 的LayoutParams 等信息计算子 View的MeasureSpec
    final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, mPaddingLeft + mPaddingRight, lp.width);
    final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, mPaddingTop + mPaddingBottom, lp.height);
    child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}

measureChild()方法通过ViewGroup的宽高和View自身的Padding属性获取到View实际大小,接下来调用View的measure()方法,下面是measure()方法的核心代码:

public final void measure(int widthMeasureSpec, int heightMeasureSpec){
    ...
    onMeasure(widthMeasureSpec, heightMeasureSpec);
    ...
}

可以看到,其实measure方法执行了对onMeasure()方法的调用,下面我们来看onMeasure()方法代码:

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
    //setMeasureDimension方法用于设置View的宽高
    setMeasureDimension(getDefaultSize(getSuggestedMinimunWidth(), widthMeasureSpec), getDefaultSize(getSuggestedMinimunHeight(), heightMeasureSpec));
}

由于measure方法是final修饰的,无法进行修改,所以在实际开发中,需要自定义控件时,我们通常会重写onMeasure()方法,如果不重写onMeasure()方法,onMeasure()方法通过调用getDefaultSize()方法获取到View最终宽高,下面我们看下getDefaultSize()方法的核心代码:

public static int getDefaultSize(int size, int measureSpec){
    int result = size;
    int specMode = MeasureSpec.getMode(measureSpec);
    int specSize = MeasureSpec.getSize(measuerSpec);
    switch (specMode){
        case MeasureSpec.UNSOECIFIED:
            result  = size;
            break;
        case MeasureSpec.AT_MOST:
        case MeasureSpec.EXACTLY:
            result = specSize;
            break;
    }
    return result;
}

这里涉及到对测量模式的选择,简单介绍一下,测量模式分为三种:
UNSPECIFIED:不指定测量模式,父视图没有限制子视图的大小,子视图可以是想要的任何尺寸,通常用于系统内部,应用开发中很少使用到。
EXACTLY:精确测量模式,当该View的layout_width和layout_height为具体数值或者为match_parent时生效,表示父视图已经决定子视图的精确大小,这种情况下View的测量值就是SpecSize的值。
AT_MOST:最大值模式,当该View的layout_width和layout_height为wrap_content时生效,此时子视图的尺寸可以是不超过父视图允许的最大尺寸的任意尺寸。

最后我们通过对整个Measure过程中参数传递流程来整体了解一下View的Measure过程
Activity启动----ViewRoot-----perTravelsals(A参数)----performMeasure(A参数)----ViewGroup----(对子View)measureChildren(A参数)----measureChild(B参数)----measure(B参数)----onMeasure(B参数)
其中A参数表示初始参数,B参数表示在A参数的基础上通过与Padding属性算出的参数;

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值