Android GUI之View測量

來源:互聯網
上載者:User

標籤:

  在上篇文章(http://blog.csdn.net/jerehedu/article/details/47081849)中,根據源碼探索了View的繪製過程,過程有三個主要步驟,分別為測量、布局、繪製。系統對繪製已經做了很好的封裝,我們主要對測量和版面配置階段進行分析,看一看android是如何對view進行測量和布局的。

  根據上篇文章的分析,我們知道在ViewRootImpl的performMeasure方法中,實際上調用了mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);方法。根據源碼我們找到了該方法的原型,此方法在View類中,並且是final方法,不可被子類重寫,方法的具體源碼如下:

public final void measure(int widthMeasureSpec, int heightMeasureSpec) {        boolean optical = isLayoutModeOptical(this);        if (optical != isLayoutModeOptical(mParent)) {          ……        }        // 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) {           ……            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                setMeasuredDimensionRaw((int) (value >> 32), (int) value);                mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;            }……            mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;        }        mOldWidthMeasureSpec = widthMeasureSpec;        mOldHeightMeasureSpec = heightMeasureSpec;        mMeasureCache.put(key, ((long) mMeasuredWidth) << 32 |                (long) mMeasuredHeight & 0xffffffffL); // suppress sign extension    }

  根據方法內容和說明,可以知道本方法就是用來測量View的大小的,而需要的兩個參數是由父View構建的,用於說明父View對子View的測量的規格要求,實際上在這個方法中真正完成測量大小的是方法onMeasure,此方法我們稍後分析。在此之前我們先要明白measure方法中的兩個參數的含義,剛才有提到參數是父View對子View的測量規格要求,那麼Android是如何描述的呢,這裡用到了一個類MeasureSpec,此類為View中的一個內部類,關鍵源碼如下:

public static class MeasureSpec {        private static final int MODE_SHIFT = 30;        private static final int MODE_MASK  = 0x3 << MODE_SHIFT;        public static final int UNSPECIFIED = 0 << MODE_SHIFT;        public static final int EXACTLY     = 1 << MODE_SHIFT;        public static final int AT_MOST     = 2 << MODE_SHIFT;        public static int makeMeasureSpec(int size, int mode) {            if (sUseBrokenMakeMeasureSpec) {                return size + mode;            } else {                return (size & ~MODE_MASK) | (mode & MODE_MASK);            }        }        public static int getMode(int measureSpec) {            return (measureSpec & MODE_MASK);        }        public static int getSize(int measureSpec) {            return (measureSpec & ~MODE_MASK);        }      ……}

  根據SDK,此類封裝了父View對子View的布局要求,每個執行個體都代表了對子View的高度或者寬度的要求,測量要求包含兩個部分,分別為尺寸和模式。模式主要由三種,具體如下:

1、  UNSPECIFIED:代表父View對子View沒有約束,子View可以為任意大小。

2、  EXACTLY:父View確定子View的大小,子View被限定在給定的邊界中,忽咯本身的大小。

3、  AT_MOST:子View最大可以達到指定大小的值。

  該類中提供了用來計算和產生測量要求的方法,具體如下:

1、  public static int makeMeasureSpec(int size, int mode),此方法最終產生一個32位位元用來表明測量規格要求,其中32和31位用來表明模式,後30位代表了大小。

2、  public static int getMode(int measureSpec),此方法可以根據測量說明,計算模式。

3、  public static int getSize(int measureSpec),此方法根據測量說明,計算大小。

  明白了MeasureSpec,我們在回過頭來,看一看onMeasure方法,該方法的源碼如下:

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

  此方法的預設實現非常簡單,調用了setMeasuredDimersion方法將測量好的尺寸儲存到mMeasuredWidth和mMeasuredHeight。而在setMeasuredDimersion方法中調用了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;    }

  很明顯,此方法根據提供的預設大小和測量要求計算View的實際大小。到此為止,View完了測量過程。不過大多數情況下,當我們自訂ViewGroup的時候,我們需要重寫onMeasure方法,在此方法中,可以遍曆所有的子View並要求他們對自己的大小進行測量,同時不要忘記調用setMeasuredDimension進行儲存測量結果,在ViewGroup是通過如下三個方法實現的,關鍵代碼如下:

  方法mesureChildren,遍曆所有的非隱藏的子View,並調用measureChild方法設定子View的測量要求。

protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {        final int size = mChildrenCount;        final View[] children = mChildren;        for (int i = 0; i < size; ++i) {            final View child = children[i];            if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {                measureChild(child, widthMeasureSpec, heightMeasureSpec);            }        }}

  方法measureChild,擷取子View的測量規格,並調用measure進行測量實際大小。

protected void measureChild(View child, int parentWidthMeasureSpec,            int parentHeightMeasureSpec) {        final LayoutParams lp = child.getLayoutParams();        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,                mPaddingLeft + mPaddingRight, lp.width);        final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,                mPaddingTop + mPaddingBottom, lp.height);        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);}

  方法getChildMeasureSpec用於擷取View的測量規格要求。

    public static int getChildMeasureSpec(int spec, int padding, int childDimension) {        int specMode = MeasureSpec.getMode(spec);        int specSize = MeasureSpec.getSize(spec);        int size = Math.max(0, specSize - padding);        int resultSize = 0;        int resultMode = 0;        switch (specMode) {        // Parent has imposed an exact size on us        case MeasureSpec.EXACTLY:            if (childDimension >= 0) {                resultSize = childDimension;                resultMode = MeasureSpec.EXACTLY;            } else if (childDimension == LayoutParams.MATCH_PARENT) {                // Child wants to be our size. So be it.                resultSize = size;                resultMode = MeasureSpec.EXACTLY;            } else if (childDimension == LayoutParams.WRAP_CONTENT) {                // Child wants to determine its own size. It can't be                // bigger than us.                resultSize = size;                resultMode = MeasureSpec.AT_MOST;            }            break;        // Parent has imposed a maximum size on us        case MeasureSpec.AT_MOST:            if (childDimension >= 0) {                // Child wants a specific size... so be it                resultSize = childDimension;                resultMode = MeasureSpec.EXACTLY;            } else if (childDimension == LayoutParams.MATCH_PARENT) {                // Child wants to be our size, but our size is not fixed.                // Constrain child to not be bigger than us.                resultSize = size;                resultMode = MeasureSpec.AT_MOST;            } else if (childDimension == LayoutParams.WRAP_CONTENT) {                // Child wants to determine its own size. It can't be                // bigger than us.                resultSize = size;                resultMode = MeasureSpec.AT_MOST;            }            break;        // Parent asked to see how big we want to be        case MeasureSpec.UNSPECIFIED:            if (childDimension >= 0) {                // Child wants a specific size... let him have it                resultSize = childDimension;                resultMode = MeasureSpec.EXACTLY;            } else if (childDimension == LayoutParams.MATCH_PARENT) {                // Child wants to be our size... find out how big it should                // be                resultSize = 0;                resultMode = MeasureSpec.UNSPECIFIED;            } else if (childDimension == LayoutParams.WRAP_CONTENT) {                // Child wants to determine its own size.... find out how                // big it should be                resultSize = 0;                resultMode = MeasureSpec.UNSPECIFIED;            }            break;        }        return MeasureSpec.makeMeasureSpec(resultSize, resultMode);    }

 

  疑問諮詢或技術交流,請加入官方QQ群: (452379712)

 

傑瑞教育
出處:http://blog.csdn.net/jerehedu/ 
本文著作權歸煙台傑瑞教育科技有限公司和CSDN共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文串連,否則保留追究法律責任的權利。 

著作權聲明:本文為博主原創文章,未經博主允許不得轉載。

Android GUI之View測量

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.