Android View-measure

来源:互联网 发布:linux 进入mysql 编辑:程序博客网 时间:2024/04/30 15:19

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 final int PFLAG_FORCE_LAYOUT 0x00001000 需要强制布局 static final int PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT 0x8 layout前需要measure static final int PFLAG_LAYOUT_REQUIRED 0x00002000 需要layout static final int PFLAG_MEASURED_DIMENSION_SET 0x00000800 设置了测量尺寸

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)方法。