View繪制的三部曲,  測量,布局,繪畫
今天我們分析測量過程

view的測量是從ViewRootImpl發(fā)起的,View需要重繪,都是發(fā)送請(qǐng)求給ViewRootImpl,然后他組織重繪
在重繪的過程中,有一步就是測量,通過代碼來分析測量過程

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開發(fā),動(dòng)畫培訓(xùn)

    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) {                //獲取測量的規(guī)格,是一個(gè)32位的二進(jìn)制數(shù)值,前兩位標(biāo)識(shí)mode,后30位表示view的長/寬
                childWidthMeasureSpec = getRootMeasureSpec(baseSize, lp.width);
                childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);                //向DecorView發(fā)起重繪                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;
    }

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開發(fā),動(dòng)畫培訓(xùn)

這個(gè)函數(shù)通過getRootMeasureSpec方法,獲取測量規(guī)格,然后調(diào)用performMeasure方法開始分發(fā)給整個(gè)的view樹。

 

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開發(fā),動(dòng)畫培訓(xùn)

    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;
    }

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開發(fā),動(dòng)畫培訓(xùn)

 

 通過MeasureSpec的makeMeasureSpec方法來生成測量規(guī)格,先判斷出布局是 match_parent 或者是 wrap_content,或者是確定的數(shù)值
 然后把windowSize傳遞下去。

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開發(fā),動(dòng)畫培訓(xùn)

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

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開發(fā),動(dòng)畫培訓(xùn)

 

 API大于17的都會(huì)走else判斷,這塊我分析一下計(jì)算結(jié)果。有助于理解后邊的運(yùn)算

 makeMeasureSpec的運(yùn)算結(jié)果是一個(gè)32位的二進(jìn)制數(shù)值,前2位表示測量的規(guī)格 EXACTLY/AT_most 后30位表示 windowSize,舉個(gè)運(yùn)算例子    
 size=320,mode=EXACTLY,換算成二進(jìn)制就是下邊的兩串值
 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
 最后用與運(yùn)算整合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

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開發(fā),動(dòng)畫培訓(xùn)

    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);
        }
    }

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開發(fā),動(dòng)畫培訓(xùn)

 

 這個(gè)mView就是我們?cè)趙indow類中組合出來的DecorView,這個(gè)方法調(diào)用了view的measure方法,measure會(huì)調(diào)用OnMeasure方法,然后就實(shí)現(xiàn)了整個(gè)view樹的測量工作

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開發(fā),動(dòng)畫培訓(xùn)

    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);
        }        // 這塊又把寬的測量規(guī)格和高的測量規(guī)格拼接在了一起,作為緩存中的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();            //判斷是否強(qiáng)制測量,如果強(qiáng)制就重新調(diào)用onMeasure,整個(gè)view樹重新測量,否則就從緩存中得到上次的測量規(guī)格,            //因?yàn)镈ecorView是FrameLayout的子類,所以onMeasure就是調(diào)用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                //調(diào)用完這個(gè)方法之后,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;        //在按照固定的格式,把本次的測量規(guī)格put到集合中
        mMeasureCache.put(key, ((long) mMeasuredWidth) << 32 |
                (long) mMeasuredHeight & 0xffffffffL); // suppress sign extension
    }

平面設(shè)計(jì)培訓(xùn),網(wǎng)頁設(shè)計(jì)培訓(xùn),美工培訓(xùn),游戲開發(fā),動(dòng)畫培訓(xùn)

 

這個(gè)方法做的功能大概就是這樣,先判斷是否有緩存,沒緩存就測量一下整個(gè)樹,然后按照固定的格式在存入緩存中

從這里開始,我們的整個(gè)測量過程就開始跑起來了

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