关于 MeasureSpec,view中measure 的整理

来源:互联网 发布:ios蜂窝移动网络搜索 编辑:程序博客网 时间:2024/05/18 16:13

1  MeasureSpec的实现。

1.1  measure是如何实现测量控件的宽高和控件的mode。

为了方便分析,我把它全部的源码,都拷出来:
  public static class MeasureSpec {        private static final int MODE_SHIFT = 30;        private static final int MODE_MASK  = 0x3 << MODE_SHIFT;        /**         * Measure specification mode: The parent has not imposed any constraint         * on the child. It can be whatever size it wants.         */        public static final int UNSPECIFIED = 0 << MODE_SHIFT;        /**         * Measure specification mode: The parent has determined an exact size         * for the child. The child is going to be given those bounds regardless         * of how big it wants to be.         */        public static final int EXACTLY     = 1 << MODE_SHIFT;        /**         * Measure specification mode: The child can be as large as it wants up         * to the specified size.         */        public static final int AT_MOST     = 2 << MODE_SHIFT;        /**         * Creates a measure specification based on the supplied size and mode.         *         * The mode must always be one of the following:         * <ul>         *  <li>{@link android.view.View.MeasureSpec#UNSPECIFIED}</li>         *  <li>{@link android.view.View.MeasureSpec#EXACTLY}</li>         *  <li>{@link android.view.View.MeasureSpec#AT_MOST}</li>         * </ul>         *         * <p><strong>Note:</strong> On API level 17 and lower, makeMeasureSpec's         * implementation was such that the order of arguments did not matter         * and overflow in either value could impact the resulting MeasureSpec.         * {@link android.widget.RelativeLayout} was affected by this bug.         * Apps targeting API levels greater than 17 will get the fixed, more strict         * behavior.</p>         *         * @param size the size of the measure specification         * @param mode the mode of the measure specification         * @return the measure specification based on size and mode         */        public static int makeMeasureSpec(int size, int mode) {            if (sUseBrokenMakeMeasureSpec) {                return size + mode;            } else {                return (size & ~MODE_MASK) | (mode & MODE_MASK);            }        }        /**         * Extracts the mode from the supplied measure specification.         *         * @param measureSpec the measure specification to extract the mode from         * @return {@link android.view.View.MeasureSpec#UNSPECIFIED},         *         {@link android.view.View.MeasureSpec#AT_MOST} or         *         {@link android.view.View.MeasureSpec#EXACTLY}         */        public static int getMode(int measureSpec) {            return (measureSpec & MODE_MASK);        }        /**         * Extracts the size from the supplied measure specification.         *         * @param measureSpec the measure specification to extract the size from         * @return the size in pixels defined in the supplied measure specification         */        public static int getSize(int measureSpec) {            return (measureSpec & ~MODE_MASK);        }        static int adjust(int measureSpec, int delta) {            return makeMeasureSpec(getSize(measureSpec + delta), getMode(measureSpec));        }
从源码可以知道,MODE_MASK 是一个整形。高两位用来测量控件的mode,其余的低位来测量控件的大小。(2^30,手机的屏幕大小有限,因此不可能有一种手机的长或者宽的像素超过这个数)
今天引入这个话题,重要的原因是,我发现目前很多人都有这么一种,我个人认为错误的想法:
如何在oncreate获取控件的长和宽。很多人都会说用view.onmeasure这个方法啊。可是这个真的能获取view的大小吗?
一切口说无凭,接下来来看源码。
public final void measure(int widthMeasureSpec, int 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);        }        // Suppress sign extension for the low bytes        long key = (long) widthMeasureSpec << 32 | (long) heightMeasureSpec & 0xffffffffL;        if (mMeasureCache == null) mMeasureCache = new LongSparseLongArray(2);        if ((mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ||                widthMeasureSpec != mOldWidthMeasureSpec ||                heightMeasureSpec != mOldHeightMeasureSpec) {            // first clears the measured dimension flag            mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET;            resolveRtlPropertiesIfNeeded();            int cacheIndex = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ? -1 :                    mMeasureCache.indexOfKey(key);            if (cacheIndex < 0 || sIgnoreMeasureCache) {                // measure ourselves, this should set the measured dimension flag back                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                setMeasuredDimension((int) (value >> 32), (int) value);                mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;            }            // flag not set, setMeasuredDimension() was not invoked, we raise            // an exception to warn the developer            if ((mPrivateFlags & PFLAG_MEASURED_DIMENSION_SET) != PFLAG_MEASURED_DIMENSION_SET) {                throw new IllegalStateException("onMeasure() did not set the"                        + " measured dimension by calling"                        + " setMeasuredDimension()");            }            mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;        }        mOldWidthMeasureSpec = widthMeasureSpec;        mOldHeightMeasureSpec = heightMeasureSpec;        mMeasureCache.put(key, ((long) mMeasuredWidth) << 32 |                (long) mMeasuredHeight & 0xffffffffL); // suppress sign extension    }
这里调用到onMeasure方法中。


 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),                getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));    }

我们看看这个getSuggestedMinimumWidth这个方法,顾名思义,它获取的应该是这个控件的最小宽度。
    protected int getSuggestedMinimumWidth() {        return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());    }

接着来看看getDefaultSize这个方法。
 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;    }
这个方法作用如下:
 如果mode为UNSPECIFIED,则用默认的大小。
如果不是,则用指定的大小。

接下来就调用setMeasuredDimension这个方法设置大小。

好了这就是整个view.measure的过程。

可是我们注意到没有,这期间,我们能获取view的实际大小吗?(我说的是此时view的实际大小:注明)
显然是不能的。

google这样设计,这个measure这个方法肯定是要让父视图调用这个方法。




1 0
原创粉丝点击