您的位置:首页 > 移动开发 > Android开发

浅析Android View的Measure过程

2015-11-07 16:47 489 查看
我们知道,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(),我们可以方便地调用;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: