×

Android MeasureSpec

96
reezy
2017.06.10 19:53 字数 245

MeasureSpec 封装了父元素对子元素宽(width)高(height)的布局需求。
MeasureSpec 由尺寸(size)与模式(mode)组成。

有以下三种测量模式:

  • EXACTLY
    指定了父元素为子元素测量的尺寸。
    宽高指定为match_parent时,模式通常为EXACTLY。
  • AT_MOST
    指定了子元素能取的最大尺寸。
    宽高指定为wrap_content时,模式通常为AT_MOST。
  • UNSPECIFIED
    父元素没有对子元素施加任何限制,它可以取任意尺寸。
    当视图的宽高值未设置或设置为0时,模式为UNSPECIFIED。

子view的尺寸由 父view的MeasureSpec子view的LayoutParams 共同决定。

下表由 ViewGroup#getChildMeasureSpec 方法总结得来:

  • 第一行为父view的测量模式
  • 第一列为子view指定的尺寸
  • size为父view指定的尺寸减去边距后的值
  • childSize为子view指定的精确值
EXACTLY AT_MOST UNSPECIFIED
childSize childSize+EXACTLY childSize+EXACTLY childSize+EXACTLY
MATCH_PARENT size+EXACTLY size+AT_MOST (0或size)+UNSPECIFIED
WRAP_CONTENT size+AT_MOST size+AT_MOST (0或size)+UNSPECIFIED

附:ViewGroup#getChildMeasureSpec源码

/**   
 * @param spec 父view的测量值(MeasureSpec)
 * @param padding 父view的边距(padding,margin) 
 * @param childDimension 子view在布局参数尺寸(LayoutParam.width/LayoutParam.height) 
 */  
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

            // View.sUseZeroUnspecifiedMeasureSpec = targetSdkVersion < M
            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

            // View.sUseZeroUnspecifiedMeasureSpec = targetSdkVersion < M
            resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
            resultMode = MeasureSpec.UNSPECIFIED;
        }
        break;
    }
    //noinspection ResourceType
    return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}

附:View#getDefaultSize 源码

// 如果测量模式为UNSPECIFIED就返回指定的尺寸(size),否则从measureSpec中获取尺寸(specSize)
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;
}
Android
Web note ad 1