Android View的measure过程详解
2017-03-05 23:38
501 查看
注意
阅读本文至少需要先了解MeasureSpec的工作原理,可以参见网上其他相关博客,本文不做解释。measure介绍
顾名思义,measure方法用于测量View的大小,由View所在的ViewGroup调用:child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
其中的 childWidthMeasureSpec 和 childHeightMeasureSpec 参数一般受ViewGroup的MeasureSpec及children的LayoutParam影响。
下面主要分两部分介绍该过程。
View的Measure过程
View的measure为final方法,其中会调用onMeasure方法,如下:protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); }
此处涉及四个方法:setMeasuredDimension,getDefaultSize,getSuggestedMinimumWidth,getSuggestedMinimumHeight下面一一介绍:
其中的setMeasureDimension方法会设置View的测量值,此处的测量值可以大致理解为View的最终大小,之所以这么说是因为View的最终大小是在layout过程中确定的,但他们基本上是相同的。
getMeasuredHeight();//measure中确定的高度 getHeight();//View的最终高度 getMeasuredWidth();//measure中确定的宽度 getWidth();//View的最终宽度
getDefaultSize方法如下:
/** * Utility to return a default size. Uses the supplied size if the * MeasureSpec imposed no constraints. Will get larger if allowed * by the MeasureSpec. * * @param size Default size for this view * @param measureSpec Constraints imposed by the parent * @return The size this view should be. */ 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; }
注释已经写的很明确了,该方法接受两个参数,第一个参数size为View的默认大小,第二个参数为父ViewGroup传过来的大小,然后在MeasureSpec的约束下获取到View的最终大小。
可以看到,View的SpecMode无论是AT_MOST还是EXACTLY,最终结果都是SpecSize。也就是说,View默认情况下,match_parent与wrap_content的值相等,也就是父布局的剩余大小最大值。
所以当我们自定义View时需要注意,如果需要让View支持wrap_content就需要重写onMeasure方法。
getSuggestedMinimumWidth与getSuggestedMinimumHeight方法工作原理相同,这里只介绍getSuggestedMinimumWidth方法:
/** * Returns the suggested minimum width that the view should use. This * returns the maximum of the view's minimum width) * and the background's minimum width * ({@link android.graphics.drawable.Drawable#getMinimumWidth()}). * <p> * When being used in {@link #onMeasure(int, int)}, the caller should still * ensure the returned width is within the requirements of the parent. * * @return The suggested minimum width of the view. */ protected int getSuggestedMinimumWidth() { return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth()); }
方法很简答,如果该View有背景则返回背景与mMinWidth的最大值,如果没有背景就返回mMinWidth。其中的mMinWidth通过setMinimumWidth或者android:minWidth指定。
ViewGroup的Measure过程:
ViewGroup除了会对自身measure外,还会对所有的子View进行measure。ViewGroup是一个抽象类,其中并没有重写onMeasure方法,所以不同的ViewGroup中对onMeasure的重写方法都有所不同。
但是ViewGroup中给我们提供了一个用于measure所有子View的方法,我们可以使用此方法measure,也可以自己写:
/** * Ask all of the children of this view to measure themselves, taking into * account both the MeasureSpec requirements for this view and its padding. * We skip children that are in the GONE state The heavy lifting is done in * getChildMeasureSpec. * * @param widthMeasureSpec The width requirements for this view * @param heightMeasureSpec The height requirements for this 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) { measureChild(child, widthMeasureSpec, heightMeasureSpec); } } }
参数为当前ViewGroup的widthMeasureSpec和heightMeasureSpec。
此方法很简单,遍历所有子View,如果View不为GONE,就对其进行measure,measureChild(child, widthMeasureSpec, heightMeasureSpec)方法如下:
/** * Ask one of the children of this view to measure itself, taking into * account both the MeasureSpec requirements for this view and its padding. * The heavy lifting is done in getChildMeasureSpec. * * @param child The child to measure * @param parentWidthMeasureSpec The width requirements for this view * @param parentHeightMeasureSpec The height requirements for this view */ 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); }
先获取子View的LayoutParam及ViewGroup的padding,然后通过getChildMeasureSpec方法获取到大小,重点在于getChildMeasureSpec方法:
/** * Does the hard part of measureChildren: figuring out the MeasureSpec to * pass to a particular child. This method figures out the right MeasureSpec * for one dimension (height or width) of one child view. * * The goal is to combine information from our MeasureSpec with the * LayoutParams of the child to get the best possible results. For example, * if the this view knows its size (because its MeasureSpec has a mode of * EXACTLY), and the child has indicated in its LayoutParams that it wants * to be the same size as the parent, the parent should ask the child to * layout given an exact size. * * @param spec The requirements for this view * @param padding The padding of this view for the current dimension and * margins, if applicable * @param childDimension How big the child wants to be in the current * dimension * @return a MeasureSpec integer for the child */ public static int getChildMeasureSpec(int spec, int padding, int childDimension) { int specMode = MeasureSpec.getMode(spec); int specSize = MeasureSpec.getSize(spec); int size = Math.max(0, specSize - padding); int resultSize = 0; int resultMode = 0; switch (specMode) { // Parent has imposed an exact size on us case MeasureSpec.EXACTLY: if (childDimension >= 0) { resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size. So be it. resultSize = size; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size. It can't be // bigger than us. resultSize = size; resultMode = MeasureSpec.AT_MOST; } break; // Parent has imposed a maximum size on us case MeasureSpec.AT_MOST: if (childDimension >= 0) { // Child wants a specific size... so be it resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } 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; resultMode = MeasureSpec.AT_MOST; } else if (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size. It can't be // bigger than us. resultSize = size; resultMode = MeasureSpec.AT_MOST; } break; // Parent asked to see how big we want to be case MeasureSpec.UNSPECIFIED: if (childDimension >= 0) { // Child wants a specific size... let him have it resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size... find out how big it should // be resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size; resultMode = MeasureSpec.UNSPECIFIED; } else if (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size.... find out how // big it should be resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size; resultMode = MeasureSpec.UNSPECIFIED; } break; } return MeasureSpec.makeMeasureSpec(resultSize, resultMode); }
上面的代码逻辑很清晰,主要是通过ViewGroup的specMode,specSize及View的LayoutParam获得View的MeasureSpec。
相关文章推荐
- Android中measure过程、view绘制原理和MeasureSpec介绍及使用详解
- Android中measure过程、WRAP_CONTENT详解以及 xml布局文件解析流程浅析
- Android中View的绘制过程 onMeasure方法简述 附有自定义View例子
- 【转】Android中View的绘制过程 onMeasure方法简述 附有自定义View例子
- Android中measure过程、WRAP_CONTENT详解以及 xml布局文件解析流程浅析
- Android中measure过程、WRAP_CONTENT详解以及xml布局文件解析流程浅析
- android绘制view的过程之一---------计算view大小(measure)
- Android中View的绘制过程 onMeasure方法简述 附有自定义View例子
- Android中View的绘制过程 onMeasure方法
- android中View.measure方法详解
- android绘制view的过程之一---------计算view大小(measure)(转)
- Android中measure过程、WRAP_CONTENT详解以及xml布局文件解析流程浅析(下)
- android绘制view的过程之一---------计算view大小(measure)
- Android中measure过程、WRAP_CONTENT详解以及xml布局文件解析流程浅析(上)
- Android中measure过程、WRAP_CONTENT详解以及xml布局文件解析流程浅析(上)
- android ViewGroup layout过程详解
- Android的View绘制过程,onMeasure()方法介绍
- android中View.measure方法详解
- Android中View的绘制过程 onMeasure方法简述 附有自定义View例子
- Android中View的绘制过程 onMeasure方法简述 附有自定义View例子