Android MeasureSpec

作者: reezy | 来源:发表于2017-06-10 19:53 被阅读42次

    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 MeasureSpec

        本文链接:https://www.haomeiwen.com/subject/wkjcqxtx.html