onMeasure()源码详细分析

2017-01-14 10:02:09来源:http://www.jianshu.com/p/5c463a254cb1作者:岛在深海处人点击

第七城市

       昨晚写的第三章 控件架构与自定义控件详解读书笔记中已经对控件树已经View的测量有了一定了解今天结合ViewGroup的测量来具体分析下onMeasure()的源码。


1.ViewGroup测量子View流程

measureChildWithMargins()/measureChild()->child.measure()->child.onMeasure-> setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); );


其中measureChildWithMargins()中调用getChildMeasureSpec()通过ViewGroup的MeasureSpec来确定子View的MeasureSpec。
measureChild( )计算父View所占空间为mPaddingLeft + mPaddingRight,即父容器左右两侧的padding值之和
measureChildWithMargins( )计算父View所占空间为mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin+ widthUsed。此处,除了父容器左右两侧的padding值之和还包括了子View左右的margin值之和( lp.leftMargin + lp.rightMargin),因为这两部分也是不能用来摆放子View的应算作父View已经占用的空间。这点从方法名measureChildWithMargins也可以看出来它是考虑了子View的margin所占空间的。
最后看下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:
result = specSize;
break;
}
return result;
}

除去第一种情况不考虑以外,可知: 在measure阶段View的宽和高由其measureSpec中的specSize决定!!


终于搞懂了一个问题,如果子View在XML布局文件中对于大小的设置采用wrap_content,那么不管父View的specMode是MeasureSpec.AT_MOST还是MeasureSpec.EXACTLY对于子View而言系统给它设置的specMode都是MeasureSpec.AT_MOST,并且其大小都是parentLeftSize即父View目前剩余的可用空间。这时wrap_content就失去了原本的意义,变成了match_parent一样了。

我们再回过头来看看getChildMeasureSpec()方法:


 public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
int specMode = View.MeasureSpec.getMode(spec);
int specSize = View.MeasureSpec.getSize(spec);
int size = Math.max(0, specSize - padding);
int resultSize = 0;
int resultMode = 0;
switch (specMode) {
case View.MeasureSpec.EXACTLY:
if (childDimension >= 0) {
resultSize = childDimension;
resultMode = View.MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
resultSize = size;
resultMode = View.MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
resultSize = size;
resultMode = View.MeasureSpec.AT_MOST;
}
break;
case View.MeasureSpec.AT_MOST:
if (childDimension >= 0) {
resultSize = childDimension;
resultMode = View.MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
resultSize = size;
resultMode = View.MeasureSpec.AT_MOST;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
resultSize = size;
resultMode = View.MeasureSpec.AT_MOST;
}
break;
case View.MeasureSpec.UNSPECIFIED:
if (childDimension >= 0) {
resultSize = childDimension;
resultMode = View.MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
resultMode = View.MeasureSpec.UNSPECIFIED;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
resultMode = View.MeasureSpec.UNSPECIFIED;
}
break;
}
return View.MeasureSpec.makeMeasureSpec(resultSize, resultMode);

}
最终解释可借助下图:




既然在ViewGroup中wrap_content失去了原本的意义,那图中的绿色标记部分,该如何处理呢?

第一种情况: 如果在xml布局中View的宽和高均用wrap_content.那么需要设置 View的宽和高为mWidth和mHeight。



第二种情况: 如果在xml布局中View的宽或高其中一个为wrap_content,那么就将该值设置为默认的宽或高,另外的一个值采用系统测量的specSize即可。
具体实现可参考:


protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec , heightMeasureSpec);
int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSpceSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSpecMode=MeasureSpec.getMode(heightMeasureSpec);
int heightSpceSize=MeasureSpec.getSize(heightMeasureSpec);
if(widthSpecMode==MeasureSpec.AT_MOST&&heightSpecMode==MeasureSpec.AT_MOST){
setMeasuredDimension(mWidth, mHeight);
}else if(widthSpecMode==MeasureSpec.AT_MOST){
setMeasuredDimension(mWidth, heightSpceSize);
}else if(heightSpecMode==MeasureSpec.AT_MOST){
setMeasuredDimension(widthSpceSize, mHeight);
}
}


参考:自定义View系列教程02--onMeasure源码详尽分析




第七城市

最新文章

123

最新摄影

微信扫一扫

第七城市微信公众平台