要理解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的值。