普通View的measure流程

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了普通View的measure流程相关的知识,希望对你有一定的参考价值。

对于普通的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函数分析结束。

以上是关于普通View的measure流程的主要内容,如果未能解决你的问题,请参考以下文章

深入了解View(一)—— measure測量流程分析

Android - View的绘制流程一(measure)

Android View measure流程详解

View的绘制流程源码分析

Android应用层View绘制流程之measure,layout,draw三步曲

Android View 测量流程(Measure)完全解析