Android View-measure

View#measure(int widthMeasureSpec, int heightMeasureSpec),Android布局机制的第一阶段,这阶段是为了了解一个View应该有多大。父节点在参数中提供约束信息。参数:

  • widthMeasureSpec:父节点对子View宽度的约束
  • heightMeasureSpec:父节点对子View高度的约束

View的实际度量工作是按此方法调用的onMeasure(int,int)进行的。因此,只有onMeasure(int,int)才能而且必须被子类覆盖。源码:

    public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
        //视觉边界模式时对widthMeasureSpec和heightMeasureSpec进行调整
        boolean optical = isLayoutModeOptical(this);
        if (optical != isLayoutModeOptical(mParent)) {
            Insets insets = getOpticalInsets();
            int oWidth  = insets.left + insets.right;
            int oHeight = insets.top  + insets.bottom;
            widthMeasureSpec  = MeasureSpec.adjust(widthMeasureSpec,  optical ? -oWidth  : oWidth);
            heightMeasureSpec = MeasureSpec.adjust(heightMeasureSpec, optical ? -oHeight : oHeight);
        }

        // 将父布局的约束条件widthMeasureSpec和heightMeasureSpec组成一个long,作为key用于缓存
        long key = (long) widthMeasureSpec << 32 | (long) heightMeasureSpec & 0xffffffffL;
        if (mMeasureCache == null) mMeasureCache = new LongSparseLongArray(2);

        //如果需要强制布局 或 新旧widthMeasureSpec、heightMeasureSpec不同
        if ((mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ||
                widthMeasureSpec != mOldWidthMeasureSpec ||
                heightMeasureSpec != mOldHeightMeasureSpec) {

            //清除 设置了测量尺寸标记
            mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET;

            //解析所有与RTL相关的属性
            resolveRtlPropertiesIfNeeded();

            //如果是强制布局cacheIndex == -1,mMeasureCache.indexOfKey(key)没有缓存返回负数
            int cacheIndex = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ? -1 :
                    mMeasureCache.indexOfKey(key);
            if (cacheIndex < 0 || sIgnoreMeasureCache) {//不使用缓存
                onMeasure(widthMeasureSpec, heightMeasureSpec);//实际度量工作在onMeasure(int,int)里
                mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;//清除 layout前需要测量标记
            } else {//使用缓存
                //获取缓存的 父布局的约束条件widthMeasureSpec和heightMeasureSpec组成的key的值
                long value = mMeasureCache.valueAt(cacheIndex);
                //设置测量尺寸,里面添加了 设置了测量尺寸标记
                setMeasuredDimensionRaw((int) (value >> 32), (int) value);
                mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;//添加 layout前需要测量标记
            }

            //没有调用setMeasuredDimension(int,int)会抛出异常
            if ((mPrivateFlags & PFLAG_MEASURED_DIMENSION_SET) != PFLAG_MEASURED_DIMENSION_SET) {
                throw new IllegalStateException("View with id " + getId() + ": "
                        + getClass().getName() + "#onMeasure() did not set the"
                        + " measured dimension by calling"
                        + " setMeasuredDimension()");
            }

            //添加 需要layout标记
            mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;
        }

        //记录新的widthMeasureSpec和heightMeasureSpec
        mOldWidthMeasureSpec = widthMeasureSpec;
        mOldHeightMeasureSpec = heightMeasureSpec;

        //将 父布局的约束条件组成的key 和 新的widthMeasureSpec和heightMeasureSpec缓存起来
        mMeasureCache.put(key, ((long) mMeasuredWidth) << 32 |
                (long) mMeasuredHeight & 0xffffffffL);
    }

方法添加了final修饰,子类有需要时应该重写onMeasure(int ,int)以完成自己的测量方式。
不了解视觉边界模式的可以看看这篇文章,Android 4.3中的视觉边界布局(Optical bounds layout)
关于onMeasure(int ,int)欢迎看看我写的Android自定义View-onMeasure介绍 - oJin的博客 - CSDN博客
关于方法里面用到的标记:

修饰符类型常量名描述
static finalintPFLAG_FORCE_LAYOUT0x00001000需要强制布局
static finalintPFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT0x8layout前需要measure
static finalintPFLAG_LAYOUT_REQUIRED0x00002000需要layout
static finalintPFLAG_MEASURED_DIMENSION_SET0x00000800设置了测量尺寸

PFLAG_FORCE_LAYOUT:在调用了requestLayout()或forceLayout()时会添加上该标记。

PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT:在measure(int,int)方法中使用缓存来设置测量尺寸时添加该标记。

PFLAG_LAYOUT_REQUIRED:在measure(int,int)方法中进行测量后添加该标记。

PFLAG_MEASURED_DIMENSION_SET:在measure(int,int)方法中进行测量前清除标记,如果测量后没有添加该标记会抛出异常。在setMeasuredDimensionRaw(int, int)中添加标记,setMeasuredDimension(int, int)中会调用setMeasuredDimensionRaw(int, int)方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值