我们知道,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(),我们可以方便地调用;