View绘制的三部曲,  测量,布局,绘画
今天我们分析测量过程

view的测量是从ViewRootImpl发起的,View需要重绘,都是发送请求给ViewRootImpl,然后他组织重绘
在重绘的过程中,有一步就是测量,通过代码来分析测量过程

平面设计培训,网页设计培训,美工培训,游戏开发,动画培训

    private boolean measureHierarchy(final View host, final WindowManager.LayoutParams lp,            final Resources res, final int desiredWindowWidth, final int desiredWindowHeight) {        int childWidthMeasureSpec;        int childHeightMeasureSpec;        boolean windowSizeMayChange = false;        if (DEBUG_ORIENTATION || DEBUG_LAYOUT) Log.v(TAG,                "Measuring " + host + " in display " + desiredWindowWidth                + "x" + desiredWindowHeight + "...");    
        boolean goodMeasure = false;        if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT) {            // On large screens, we don't want to allow dialogs to just            // stretch to fill the entire width of the screen to display            // one line of text.  First try doing the layout at a smaller            // size to see if it will fit.
            final DisplayMetrics packageMetrics = res.getDisplayMetrics();
            res.getValue(com.android.internal.R.dimen.config_prefDialogWidth, mTmpValue, true);            int baseSize = 0;            if (mTmpValue.type == TypedValue.TYPE_DIMENSION) {
                baseSize = (int)mTmpValue.getDimension(packageMetrics);
            }            if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": baseSize=" + baseSize);            if (baseSize != 0 && desiredWindowWidth > baseSize) {                //获取测量的规格,是一个32位的二进制数值,前两位标识mode,后30位表示view的长/宽
                childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
                childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);                //向DecorView发起重绘                performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);                if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": measured ("
                        + host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")");                if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {
                    goodMeasure = true;
                } else {                    // Didn't fit in that size... try expanding a bit.
                    baseSize = (baseSize+desiredWindowWidth)/2;                    if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": next baseSize="
                            + baseSize);
                    childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
                    performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);                    if (DEBUG_DIALOG) Log.v(TAG, "Window " + mView + ": measured ("
                            + host.getMeasuredWidth() + "," + host.getMeasuredHeight() + ")");                    if ((host.getMeasuredWidthAndState()&View.MEASURED_STATE_TOO_SMALL) == 0) {                        if (DEBUG_DIALOG) Log.v(TAG, "Good!");
                        goodMeasure = true;
                    }
                }
            }
        }        if (!goodMeasure) {
            childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);
            childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
            performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);            if (mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()) {
                windowSizeMayChange = true;
            }
        }        if (DBG) {
            System.out.println("======================================");
            System.out.println("performTraversals -- after measure");
            host.debug();
        }        return windowSizeMayChange;
    }

平面设计培训,网页设计培训,美工培训,游戏开发,动画培训

这个函数通过getRootMeasureSpec方法,获取测量规格,然后调用performMeasure方法开始分发给整个的view树。

 

平面设计培训,网页设计培训,美工培训,游戏开发,动画培训

    private static int getRootMeasureSpec(int windowSize, int rootDimension) {        int measureSpec;        switch (rootDimension) {        case ViewGroup.LayoutParams.MATCH_PARENT:            // Window can't resize. Force root view to be windowSize.
            measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);            break;        case ViewGroup.LayoutParams.WRAP_CONTENT:            // Window can resize. Set max size for root view.
            measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);            break;        default:            // Window wants to be an exact size. Force root view to be that size.
            measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);            break;
        }        return measureSpec;
    }

平面设计培训,网页设计培训,美工培训,游戏开发,动画培训

 

 通过MeasureSpec的makeMeasureSpec方法来生成测量规格,先判断出布局是 match_parent 或者是 wrap_content,或者是确定的数值
 然后把windowSize传递下去。

平面设计培训,网页设计培训,美工培训,游戏开发,动画培训

    public static int makeMeasureSpec(int size, int mode) {            if (sUseBrokenMakeMeasureSpec) {                return size + mode;
            } else {                return (size & ~MODE_MASK) | (mode & MODE_MASK);
            }
    }

平面设计培训,网页设计培训,美工培训,游戏开发,动画培训

 

 API大于17的都会走else判断,这块我分析一下计算结果。有助于理解后边的运算

 makeMeasureSpec的运算结果是一个32位的二进制数值,前2位表示测量的规格 EXACTLY/AT_most 后30位表示 windowSize,举个运算例子    
 size=320,mode=EXACTLY,换算成二进制就是下边的两串值
 size=0000 0000 0000 0000 0000 0001 0100 0000
 mode=0100 0000 0000 0000 0000 0000 0000 0000
 mask=1100 0000 0000 0000 0000 0000 0000 0000
 最后用与运算整合size和mode
 0000 0000 0000 0000 0000 0001 0100 0000 |
 0100 0000 0000 0000 0000 0000 0000 0000 =
 0100 0000 0000 0000 0000 0001 0100 0000

平面设计培训,网页设计培训,美工培训,游戏开发,动画培训

    private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");        try {
            mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
    }

平面设计培训,网页设计培训,美工培训,游戏开发,动画培训

 

 这个mView就是我们在window类中组合出来的DecorView,这个方法调用了view的measure方法,measure会调用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);
        }        // 这块又把宽的测量规格和高的测量规格拼接在了一起,作为缓存中的key
        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();            //判断是否强制测量,如果强制就重新调用onMeasure,整个view树重新测量,否则就从缓存中得到上次的测量规格,            //因为DecorView是FrameLayout的子类,所以onMeasure就是调用FrameLayout的onMeasure方法
            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                //调用完这个方法之后,getMeasuredWidth和getMeasuredHeight就可以取到值了
                setMeasuredDimensionRaw((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("View with id " + getId() + ": "
                        + getClass().getName() + "#onMeasure() did not set the"
                        + " measured dimension by calling"
                        + " setMeasuredDimension()");
            }

            mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;
        }

        mOldWidthMeasureSpec = widthMeasureSpec;
        mOldHeightMeasureSpec = heightMeasureSpec;        //在按照固定的格式,把本次的测量规格put到集合中
        mMeasureCache.put(key, ((long) mMeasuredWidth) << 32 |
                (long) mMeasuredHeight & 0xffffffffL); // suppress sign extension
    }

平面设计培训,网页设计培训,美工培训,游戏开发,动画培训

 

这个方法做的功能大概就是这样,先判断是否有缓存,没缓存就测量一下整个树,然后按照固定的格式在存入缓存中

从这里开始,我们的整个测量过程就开始跑起来了

http://www.cnblogs.com/kezhuang/p/7119859.html

延伸阅读

告别“老顽固”-Java培训,做最负责任的教育,学习改变命运,软件学习,再就业,大学生如何就业,帮大学生找到好工作,lphotoshop培训,电脑培训,电脑维修培训,移动软件开发培训,网站设计培训,网站建设培训告别“老顽固”