View的measure函数
public final void measure(int widthMeasureSpec, int heightMeasureSpec) { if ((mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT || widthMeasureSpec != mOldWidthMeasureSpec || heightMeasureSpec != mOldHeightMeasureSpec) { // first clears the measured dimension flag mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET; resolveRtlPropertiesIfNeeded(); // measure ourselves, this should set the measured dimension flag back onMeasure(widthMeasureSpec, heightMeasureSpec); // flag not set, setMeasuredDimension() was not invoked, we raise // an exception to warn the developer if ((mPrivateFlags & PFLAG_MEASURED_DIMENSION_SET) != PFLAG_MEASURED_DIMENSION_SET) { throw new IllegalStateException("onMeasure() did not set the" + " measured dimension by calling" + " setMeasuredDimension()"); } mPrivateFlags |= PFLAG_LAYOUT_REQUIRED; } mOldWidthMeasureSpec = widthMeasureSpec; mOldHeightMeasureSpec = heightMeasureSpec; }
参数widthMeasureSpec和heightMeasureSpec由父View构建,表示父View给子View的测量要求,这个值就是根据XML里面设置的View的layout_width和layout_height来构建的(其实不只是这样的),总之,这两个参数是父布局传给子View的。
DecorView根布局的参数widthMeasureSpec和heightMeasureSpec在代码里面初始化的时候给出。
onMeasure的默认实现
// widthMeasureSpec和heightMeasureSpec 为父类传过来的宽高。 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); }
getDefaultSize实现
public static int getDefaultSize(int size, int measureSpec) { int result = size; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); switch (specMode) { case MeasureSpec.UNSPECIFIED: result = size; break; case MeasureSpec.AT_MOST: case MeasureSpec.EXACTLY: // wrap_content会封装成AT_MOST 具体的精确值或者match_parent会封装成EXACTLY(后面会看到) //因此可以看出默认的测量实现中设置成包裹内容(wrap_content)和精确值都会将宽高设置成MeasureSpec中封装的size值 result = specSize; break; } return result; }
ViewGroup:View的measure的参数就是父布局传过来的,而ViewGroup又是继承自View的,那么他的measure的参数其实也是父类传过来的。DecorView根布局的参数widthMeasureSpec和heightMeasureSpec在代码里面初始化的时候给出。
//某个ViewGroup类型的视图 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //必须调用super.ononMeasure()或者直接调用setMeasuredDimension()方法设置该View大小,否则会报异常。 super.onMeasure(widthMeasureSpec , heightMeasureSpec) //setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), // getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); //遍历每个子View for(int i = 0 ; i < getChildCount() ; i++){ View child = getChildAt(i); //调用子View的onMeasure,默认的情况又回到了上面的实现。 //这个只是示意一下大致的逻辑 child.onMeasure(childWidthMeasureSpec, childHeightMeasureSpec); } }
ViewGroup提供了measureChildren来进行测量
//widthMeasureSpec 和 heightMeasureSpec 表示该父View的布局要求 //遍历每个子View,然后调用measureChild()方法去实现每个子View大小 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) { // 不处于 “GONE” 状态 measureChild (child, widthMeasureSpec, heightMeasureSpec); }
//测量每个子View高宽时,清楚了该View本身的边距大小,即android:padding属性 或android:paddingLeft等属性标记 protected void measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec) { final LayoutParams lp = child.getLayoutParams(); // LayoutParams属性 //设置子View的childWidthMeasureSpec属性,去除了该父View的边距值 mPaddingLeft + mPaddingRight final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, mPaddingLeft + mPaddingRight, lp.width); //设置子View的childHeightMeasureSpec属性,去除了该父View的边距值 mPaddingTop + mPaddingBottom final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, mPaddingTop + mPaddingBottom, lp.height); //可以很清楚的看到传递给子View的两个参数是由父类确定的,由getChildMeasureSpec得到的。 child.measure(childWidthMeasureSpec, childHeightMeasureSpec); }
还有和measureChild类似的函数,比如measureChildWithMargins
protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) { final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin + widthUsed, lp.width); final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin + heightUsed, lp.height); child.measure(childWidthMeasureSpec, childHeightMeasureSpec); }
getChildMeasureSpec// spec参数表示该父View本身所占的widthMeasureSpec 或 heightMeasureSpec值 // padding参数表示该父View的边距大小,见于android:padding属性 或android:paddingLeft等属性标记 // childDimension参数表示该子View内部LayoutParams属性的值,可以是wrap_content、match_parent、一个精确指(an exactly size), public static int getChildMeasureSpec(int spec, int padding, int childDimension) { int specMode = MeasureSpec.getMode(spec); //获得父View的mode int specSize = MeasureSpec.getSize(spec); //获得父View的实际值 int size = Math.max(0, specSize - padding); //父View为子View设定的大小,减去边距值, int resultSize = 0; int resultMode = 0; switch (specMode) { // Parent has imposed an exact size on us //1、父View是EXACTLY的 ! case MeasureSpec.EXACTLY: //1.1、子View的width或height是个精确值 (an exactly size) if (childDimension >= 0) { resultSize = childDimension; //size为精确值 resultMode = MeasureSpec.EXACTLY; //mode为 EXACTLY 。 } //1.2、子View的width或height为 MATCH_PARENT/FILL_PARENT else if (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size. So be it. resultSize = size; //size为父视图大小 resultMode = MeasureSpec.EXACTLY; //mode为 EXACTLY 。 } //1.3、子View的width或height为 WRAP_CONTENT else if (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size. It can't be // bigger than us. resultSize = size; //size为父视图大小 resultMode = MeasureSpec.AT_MOST; //mode为AT_MOST 。 } break; // Parent has imposed a maximum size on us //2、父View是AT_MOST的 ! case MeasureSpec.AT_MOST: //2.1、子View的width或height是个精确值 (an exactly size) if (childDimension >= 0) { // Child wants a specific size... so be it resultSize = childDimension; //size为精确值 resultMode = MeasureSpec.EXACTLY; //mode为 EXACTLY 。 } //2.2、子View的width或height为 MATCH_PARENT/FILL_PARENT else if (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size, but our size is not fixed. // Constrain child to not be bigger than us. resultSize = size; //size为父视图大小 resultMode = MeasureSpec.AT_MOST; //mode为AT_MOST } //2.3、子View的width或height为 WRAP_CONTENT else if (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size. It can't be // bigger than us. resultSize = size; //size为父视图大小 resultMode = MeasureSpec.AT_MOST; //mode为AT_MOST } break; // Parent asked to see how big we want to be //3、父View是UNSPECIFIED的 ! case MeasureSpec.UNSPECIFIED: //3.1、子View的width或height是个精确值 (an exactly size) if (childDimension >= 0) { // Child wants a specific size... let him have it resultSize = childDimension; //size为精确值 resultMode = MeasureSpec.EXACTLY; //mode为 EXACTLY } //3.2、子View的width或height为 MATCH_PARENT/FILL_PARENT else if (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size... find out how big it should // be resultSize = 0; //size为0! ,其值未定 resultMode = MeasureSpec.UNSPECIFIED; //mode为 UNSPECIFIED } //3.3、子View的width或height为 WRAP_CONTENT else if (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size.... find out how // big it should be resultSize = 0; //size为0! ,其值未定 resultMode = MeasureSpec.UNSPECIFIED; //mode为 UNSPECIFIED } break; } //根据上面逻辑条件获取的mode和size构建MeasureSpec对象。 return MeasureSpec.makeMeasureSpec(resultSize, resultMode); }
getChildMeasureSpec(int spec, int padding, int childDimension)函数表示传递给子View的onMeasure函数中的参数是由父布局和子布局共同决定的。
Spec参数表示父View本身的widthMeasureSpec或heightMeasureSpec值,
childDimension参数表示该子View内部LayoutParams属性的值,可以是wrap_content、match_parent、一个精确值(an exactly size)
最外层的View的onMeasure函数中的第一个参数是在代码中初始化时给出的
childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width); childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
private int getRootMeasureSpec(int windowSize, int rootDimension) { int measureSpec; switch (rootDimension) { case ViewGroup.LayoutParams.MATCH_PARENT: measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY); break; case ViewGroup.LayoutParams.WRAP_CONTENT: measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST); break; default: measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY); break; } return measureSpec; }
使用了MeasureSpec.makeMeasureSpec()方法来组装一个MeasureSpec,当rootDimension参数等于MATCH_PARENT的时候,MeasureSpec的specMode就等于EXACTLY,当rootDimension等于WRAP_CONTENT的时候,MeasureSpec的specMode就等于AT_MOST。并且MATCH_PARENT和WRAP_CONTENT时的specSize都是等于windowSize的,也就意味着根视图总是会充满全屏的。
lp.width是MATCH_PARENT,因此测量得到的measureSpec为屏幕大小的EXACTLY,也就是说,最顶层的View的onMeasure函数里面的参数为屏幕尺寸是EXACTLY的。
经过上面的分析可以看出果然是:传递给View的onMeasure的widthMeasureSpec和heightMeasureSpec值是经过父布局计算的。
getChildMeasureSpec函数的实际意义如下图所示:
参考文章:
http://blog.csdn.net/fengye810130/article/details/9181531
http://blog.csdn.net/guolin_blog/article/details/16330267