通过源码分析View的测量

时间:2022-12-28 08:49:53

 

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