通过源码分析View的测量
要理解View的测量,首先要了解MeasureSpec,系统在测量view的宽高时,要先确定MeasureSpec。
MeasureSpec(32为int值)由两部分组成:
SpecMode(高2位):测量模式。
SpecSize(低30位):某种测量模式下的规格大小。
SpecMode有3类:
UNSPECIFIED: 父容器不对view做大小限制,一般用于系统内部,表示一种测量状态。
EXACTLY:精确模式。对应于:LayoutPrams中的match_parent和具体数值。
AT_MOST:最大值模式。对应于LayoutParam中的wrap_content模式。
接下来我们看看View的onMeasure方法:
/** * <p> * Measure the view and its content to determine the measured width and the * measured height. This method is invoked by {@link #measure(int, int)} and * should be overridden by subclasses to provide accurate and efficient * measurement of their contents. * </p> * protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); }
该方法是由measure来调用的,view宽高的测量值最终是通过setMeasuredDimension来设置,具体实现不去深究。
我们看看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; }
先从MeasureSpec中获取的测量模式specMode和该模式下的大小specSize,然后对测量模式进行判断,如果是UNSPECIFIED,那么结果就是参数size,对于这个size是什么等会讨论。如果是AT_MOST和EXACTLY,那么结果就是view测量后的大小specSize。
那么这个参数size到底是什么呢?它是由实参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设置了背景,那么就返回max(mMinWidth, mBackground.getMinimumWidth()),否则返回mMinWidth。
mMinWidth是什么呢?它是对应android:minWidth这个属性的值,若是不指定,那么默认为0.
max(mMinWidth, mBackground.getMinimumWidth()):返回mMinWidth和mBackground.getMinimumWidth()两者中的最大值。
mBackground.getMinimumWidth()的代码如下:
/** * Returns the minimum width suggested by this Drawable. If a View uses this * Drawable as a background, it is suggested that the View use at least this * value for its width. (There will be some scenarios where this will not be * possible.) This value should INCLUDE any padding. * * @return The minimum width suggested by this Drawable. If this Drawable * doesn't have a suggested minimum width, 0 is returned. */ public int getMinimumWidth() { final int intrinsicWidth = getIntrinsicWidth(); return intrinsicWidth > 0 ? intrinsicWidth : 0; }
它返回的是Drawable的原始宽度,如果Drawable有原始宽度的话。否则返回0.
因此若view有设置背景,那么size就是android:minWidth和背景最小宽度这两者的最大值,若没有设置背景,那么size的值就是android:minWidth的值。