对于普通的view,其测量在ViewGroup中的measureChildWithMargins函数中调用child view的measure开始测量。
1:从measure函数开始
1 public final void measure(int widthMeasureSpec, int heightMeasureSpec) { 2 if ((mPrivateFlags & FORCE_LAYOUT) == FORCE_LAYOUT || 3 widthMeasureSpec != mOldWidthMeasureSpec || 4 heightMeasureSpec != mOldHeightMeasureSpec) { 5 6 // first clears the measured dimension flag 7 mPrivateFlags &= ~MEASURED_DIMENSION_SET; 8 9 if (ViewDebug.TRACE_HIERARCHY) { 10 ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_MEASURE); 11 } 12 13 // measure ourselves, this should set the measured dimension flag back 14 onMeasure(widthMeasureSpec, heightMeasureSpec); 15 16 // flag not set, setMeasuredDimension() was not invoked, we raise 17 // an exception to warn the developer 18 if ((mPrivateFlags & MEASURED_DIMENSION_SET) != MEASURED_DIMENSION_SET) { 19 throw new IllegalStateException("onMeasure() did not set the" 20 + " measured dimension by calling" 21 + " setMeasuredDimension()"); 22 } 23 24 mPrivateFlags |= LAYOUT_REQUIRED; 25 } 26 27 mOldWidthMeasureSpec = widthMeasureSpec; 28 mOldHeightMeasureSpec = heightMeasureSpec; 29 }
官方关于此函数的说明:measure函数用来计算一个view的尺寸,其传入的参数为parentView对此view的宽/高限制信息。实际的尺寸测量将会调用onMeasure来完成,因此,子类必须重写onMeasure函数。
通过measure函数的源码我们也可以知道:首先,它是final的,所以不可以重写;其次,它主要是对传入的parentView的宽高限制信息进行了是否与上一次的相同的判断,若是相同则不调用onMeasure重新测量;
2:measure ——> onMeasure函数
1 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 2 setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), 3 getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); 4 }
通过其代码,我们可以知道主要用了这三个函数,我们一一分析:
2.1:getSuggestedMinimumWidth函数:返回android:minWidth和背景宽度二者之间的最大值
1 protected int getSuggestedMinimumWidth() { 2 int suggestedMinWidth = mMinWidth; //mMinWidth对应android:minWidth属性所指定的值,如果它没有指定,那么默认为0 3 4 if (mBGDrawable != null) { //如果设置了背景 5 final int bgMinWidth = mBGDrawable.getMinimumWidth(); //背景的宽度,如果是ShapeDrawable那么为0,如果是BitmapDrawable则有原始宽高 6 if (suggestedMinWidth < bgMinWidth) { //如果设定的最小值小于背景图片宽度,那么设定最小值为背景图片宽度 7 suggestedMinWidth = bgMinWidth; 8 } 9 } 10 11 return suggestedMinWidth; 12 }
2.2:getDefaultSize:如果measureSpec没有限定则返回size,否则返回measureSpec中的size
1 public static int getDefaultSize(int size, int measureSpec) { 2 int result = size; 3 int specMode = MeasureSpec.getMode(measureSpec); 4 int specSize = MeasureSpec.getSize(measureSpec); 5 6 switch (specMode) { 7 case MeasureSpec.UNSPECIFIED: 8 result = size; 9 break; 10 case MeasureSpec.AT_MOST: 11 case MeasureSpec.EXACTLY: 12 result = specSize; 13 break; 14 } 15 return result; 16 }
到此:我们可以总结getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec)的逻辑:如果parentView对尺寸没有限制(即parentView的模式为UNSPECIFIED),那么将返回view的suggested最小值,否则返回parentView指定的尺寸。getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)类似。
2.3 setMeasuredDimension:很简单,设置测量的宽高(注意不是view实际的宽高,实际的宽高要等layout完成之后才确定,虽然几乎全部就是measure后的宽高)
1 protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) { 2 mMeasuredWidth = measuredWidth; 3 mMeasuredHeight = measuredHeight; 4 5 mPrivateFlags |= MEASURED_DIMENSION_SET; 6 }
至此,关于onMeasure函数分析结束。