自定义 View:Measure 过程

1、作用
  1. 作用:测量 View 的宽高。
  2. 注意:有些情况需要多次测量,measure 过程得到的宽高可能不准确;onLayout 获取最终宽高。
2、关键类:LayoutParams
  1. 作用:指定视图宽高等布局参数。
  2. 参数:layout_height = match_parent / wrap_content / 10dp 等。
2、关键类:MeasureSpecs
  1. 作用:指定视图测量规格。
  2. 组成:测量规格(32位) = 测量模式(高2位) + 测量大小(低30位)。(MeasureSpecs = mode + size)。
模式作用应用
UNSPECIFIED父视图不约束子视图ListView、ScrollView
EXACTLY确切的尺寸match_parent、10dp
AT_MOST自适应大小wrap_content
	// View 的 内部类 MeasureSpec 类
	
    public static class MeasureSpec {
        private static final int MODE_SHIFT = 30;
        // 11 + 30个0
        private static final int MODE_MASK  = 0x3 << MODE_SHIFT;
		// 00 + 30个0
        public static final int UNSPECIFIED = 0 << MODE_SHIFT;
		// 01 + 30个0
        public static final int EXACTLY     = 1 << MODE_SHIFT;
		// 10 + 30个0
        public static final int AT_MOST     = 2 << MODE_SHIFT;
		
		// 获取 MeasureSpec 值
        public static int makeMeasureSpec(@IntRange(from = 0, to = (1 << MeasureSpec.MODE_SHIFT) - 1) int size,
                                          @MeasureSpecMode int mode) {
            if (sUseBrokenMakeMeasureSpec) {
                return size + mode;
            } else {
                return (size & ~MODE_MASK) | (mode & MODE_MASK);
            }
        }

        public static int makeSafeMeasureSpec(int size, int mode) {
            if (sUseZeroUnspecifiedMeasureSpec && mode == UNSPECIFIED) {
                return 0;
            }
            return makeMeasureSpec(size, mode);
        }
		
		// 获取测量模式 即 高2位。
        public static int getMode(int measureSpec) {
            //noinspection ResourceType
            return (measureSpec & MODE_MASK);
        }
		// 获取测量大小 即 低30位。
        public static int getSize(int measureSpec) {
            return (measureSpec & ~MODE_MASK);
        }
   
    }

  1. 子 view 的 MeasureSpecs = 父 view 的 MeasureSpecs + 子 view 的 LayoutParams 共同决定。
	// ViewGroup.java
	
	// spec 父view的测量规格;padding 子view的边距,内边距和外边距(padding、margin);childDimension 子view的布局参数。
    public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
    	// 父view的测量模式和测量大小
        int specMode = MeasureSpec.getMode(spec);
        int specSize = MeasureSpec.getSize(spec);
		
		// 父大小 - 边距
        int size = Math.max(0, specSize - padding);

        int resultSize = 0;
        int resultMode = 0;
		
		// 子view的大小 = 父view的 MeasureSpec 和 子view的 LayoutParams 决定。
        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
                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
                resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                resultMode = MeasureSpec.UNSPECIFIED;
            }
            break;
        }
        //noinspection ResourceType
        return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
    }
3、Measure 过程
  1. View 的测量过程:measure->onMeasure->getDefaultSize->setMeasuredDimension。
	// View.java
	// 参数:宽/高 测量规格
    public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
     	。。。
            int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key);
            if (cacheIndex < 0 || sIgnoreMeasureCache) {
                // 测量视图大小
                onMeasure(widthMeasureSpec, heightMeasureSpec);
                mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
            } else {
                long value = mMeasureCache.valueAt(cacheIndex);
                // Casting a long to int drops the high 32 bits, no mask needed
                setMeasuredDimensionRaw((int) (value >> 32), (int) value);
                mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
            }
	。。。
    }
	
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
                getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
    }

	// 参数:测量后的 宽/高
    protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
        boolean optical = isLayoutModeOptical(this);
        if (optical != isLayoutModeOptical(mParent)) {
            Insets insets = getOpticalInsets();
            int opticalWidth  = insets.left + insets.right;
            int opticalHeight = insets.top  + insets.bottom;

            measuredWidth  += optical ? opticalWidth  : -opticalWidth;
            measuredHeight += optical ? opticalHeight : -opticalHeight;
        }
        setMeasuredDimensionRaw(measuredWidth, measuredHeight);
    }

    private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {
        mMeasuredWidth = measuredWidth;
        mMeasuredHeight = measuredHeight;

        mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;
    }
	// 参数:view 的大小;测量规格。
    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;
    }
	// getMinimumWidth:背景图的宽高 BitmapDrawable 有原始宽高,ShapeDrawable 没有原始宽高
    protected int getSuggestedMinimumWidth() {
        return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());
    }
  1. ViewGroup 的测量过程:measure->onMeasure(重写)->getChildMeasureSpec->遍历子view测量合并->setMeasuredDimension。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值