浅析Android View的Measure过程

来源:互联网 发布:印度红色走廊 知乎 编辑:程序博客网 时间:2024/06/05 22:34
我们知道,Android的View从创建到展示给用户的过程包含了三个阶段:measure阶段、layout阶段、和draw阶段,即测量、布局、绘制。而measure则是其中之首,它测量并确定了view的宽高。当我们自定义的View的时候应该怎么样处理measure过程,重写measure过程的哪些方法呢?带着这些疑问,现简单总结下View的measure过程。

measure过程较为复杂,但核心方法只有两个:onMeasure()和measure() 方法,其中measure()方法是final的,不可重写,它调用了onMeasure()方法,在自定义View的时候我们可以重写onMeasure()方法。可以看到,这是典型的模板方法模式。

onMeasure() 方法:

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {    setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),            getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));}
和measure() 方法:

public final void measure(int widthMeasureSpec, int heightMeasureSpec) {    boolean optical = isLayoutModeOptical(this);    if (optical != isLayoutModeOptical(mParent)) {        Insets insets = getOpticalInsets();        int oWidth  = insets.left + insets.right;        int oHeight = insets.top  + insets.bottom;        widthMeasureSpec  = MeasureSpec.adjust(widthMeasureSpec,  optical ? -oWidth  : oWidth);        heightMeasureSpec = MeasureSpec.adjust(heightMeasureSpec, optical ? -oHeight : oHeight);    }.....    onMeasure(widthMeasureSpec, heightMeasureSpec);}

我们可以看到,measure()方法是final的,这意味着子类不能够重写这个方法,measure()是整个measure过程的核心,它测量了该view的宽高,并将宽高存放在mMeasuredwidth和mMeasuredHeight中(注:此时View中的mHeight和mWidth都未初始化,都还是0),有兴趣的朋友可以阅读源码来具体看看mMeasureWidth和mMeasuredHeight是如何初始化。此外,measure()方法内部还会调用onMeasure()方法,我们可以重写onMeasure()方法来进行其它测量操作,最常见的就是对子View进行测量。一般来说,在ViewGroup中,不仅要对自己的大小进行测量,还要依次调用各个子View的measure过程来对子View进行测量,因此我们在自定义ViewGroup时就需要重写onMeasure方法来对子类进行Measure。事实上,ViewGroup基类并没有重写onMeasure方法,只是提供了measureChildren方法来调用每个子view的measure():
ViewGroup.measureChildren()
protected 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];        if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {            measureChild(child, widthMeasureSpec, heightMeasureSpec);        }    }}
measureChildren则会调用measureChild,measureChild方法则十分简单,只是获取了子view的layoutParam,调用了子view的measure方法:
protected void measureChild(View child, int parentWidthMeasureSpec,        int parentHeightMeasureSpec) {    final LayoutParams lp = child.getLayoutParams();    final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,            mPaddingLeft + mPaddingRight, lp.width);    final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,            mPaddingTop + mPaddingBottom, lp.height);    child.measure(childWidthMeasureSpec, childHeightMeasureSpec);}

因此,当我们在自定义布局重构measure过程时,只需要在onMeasure()方法中调用measureChildren(),就可以递归调用子类的measure过程了。


总结如下:

1、measure过程是View绘制的三大流程之首,优先于layout过程和draw过程;

2、measure过程的核心是measure()方法,measure()方法对自己的宽高进行测量,初始化mMeasuredWidth和mMeasuredHeight,并调用onMeasure方法,此时layout过程还未调用,与layout过程有关的参数都未初始化;
3、重写ViewGroup的onMeasure()方法时,在计算自己的大小同时,还要调用子View的measure,这一步骤被封装成了measureChildern(),我们可以方便地调用



0 0
原创粉丝点击