這段時(shí)間在學(xué)習(xí)android中view的工作原理與自定義View的相關(guān)內(nèi)容,所以未來(lái)這這幾篇博客都總結(jié)一下相關(guān)的知識(shí)吧。

首先我們要了解和熟悉兩個(gè)概念,DecorView 與 MeasureSpec.

DecorView

我們?cè)谠O(shè)置Activity的界面時(shí),用的就是這句話 setContentView(R.layout.activity_main),那么大家有沒(méi)有疑問(wèn)呢,這個(gè)名字有點(diǎn)奇怪啊,為什么是setContentView?難道不應(yīng)該是setView嗎?這個(gè)問(wèn)題就要從DecorView來(lái)說(shuō)起了。源碼就不追蹤了,直接說(shuō)結(jié)論。

  1. DecorView就是我們的 Activity(或者整個(gè)Window界面)的最頂級(jí)View。

  2. DecorView extends FrameLayout,它里面只有一個(gè)子元素為 LinearLayout,代表整個(gè) Window 界面。

  3. LinearLayout布局也包裹了兩個(gè)FrameLayout。

    • 上面的Framelayout是標(biāo)題欄(TitleBarActionBar)。具體形式和android版本及主題有關(guān)。它默認(rèn)包括了一個(gè)TextView,當(dāng)然我們也可以自定義標(biāo)題欄。

    • 下面的 FrameLayout 就是內(nèi)容欄。它的id就是 content,我們通過(guò)setContentView所設(shè)置的布局文件其實(shí)就是添加在內(nèi)容欄當(dāng)中,所以這個(gè)方法就是叫做 setContentView。

因此現(xiàn)在我們可以體會(huì)到這個(gè)方法的命名的確很好。所以我們開(kāi)發(fā)的命名也盡量做到望名知意,有理有據(jù).

There are only two hard things in Computer Science: cache invalidation and naming things.
-- Phil Karlton

現(xiàn)在我們就知道所謂的DecorView是怎么一回事了,google了一張圖片可以比較清晰的表達(dá)這個(gè)問(wèn)題。

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

其中圖片的綠色部分,就是我們?cè)诓季种袑?xiě)的 布局文件了。

默認(rèn)標(biāo)題欄只有一個(gè)TextView,我們也是可以自定義的。

    //@author www.yaoxiaowen.com  三句話的順序不要顛倒。
    requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);    setContentView(R.layout.custom_title);    getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.custom_title_layout);

那我們?nèi)绾蔚玫?code style="margin: 0px; padding: 0.2em 0px; box-sizing: border-box; line-height: 1.8; vertical-align: middle; display: inline-block; font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; font-size: 12px; background-color: rgba(0, 0, 0, 0.04); border: none !important; border-radius: 3px;">content這個(gè)view呢。

ViewGroup content = (ViewGroup)findViewById(android.R.id.content);

那我們又如何得到我們添加的布局viewGroup呢?

//結(jié)合上一句代碼content.getChildAt(0);

MeasureSpec

在討論MeasureSpec之前,我們先回憶一下,在布局中,我們是怎么設(shè)置一個(gè)View的尺寸大小的。

        //height類似,所以就不寫(xiě)了,僅拿 width舉例子 
        android:layout_width="wrap_content"
        android:layout_width="match_parent"
        android:layout_width="100dp"

我們可以通過(guò) LayoutParams來(lái)設(shè)置view的尺寸,但是僅依靠我們?cè)O(shè)置的參數(shù),能完全決定view的尺寸嗎? 肯定是不能的。因?yàn)橐粋€(gè)View大小還要受到父ViewGroup的影響。

一個(gè)人的命運(yùn),既要靠自己奮斗, 也要考慮歷史進(jìn)程。所以一個(gè)view的尺寸大小,既要考慮自己所希望的大小,但是也要考慮父ViewGroup對(duì)其所施加的影響。否則如果View自己就能完全決定自己尺寸的大小,那真是沒(méi)王法了。

MeasureSpec其實(shí)就是View當(dāng)中的一個(gè)靜態(tài)工具類,翻譯過(guò)來(lái)就是 測(cè)量說(shuō)明書(shū)。 代表了View在測(cè)試過(guò)程中所受到的約束。

View的測(cè)量過(guò)程中,系統(tǒng)將View自身的 LayoutParams結(jié)合父容器所施加的規(guī)則轉(zhuǎn)換成對(duì)應(yīng)的MeasureSpec,然后再根據(jù)這個(gè)MeasureSpec來(lái)測(cè)量出View的尺寸寬高 。

MeasureSpec代表了一個(gè)32位的int類型的值。高兩位是SpecMode(測(cè)量模式), 低30位是 SpecSize(尺寸規(guī)格大小)。將SpecModeSpecSize通過(guò)位操作封裝成一個(gè)int值,可以減少內(nèi)存分配,提升效率。 而MeasureSpec提供了打包,解包,toString等方法。可以方便操作。

精簡(jiǎn)后的源碼如下:

//View#MeasureSpec 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;  //0

        /**         * 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;  //1073741824

        /**         * 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;  //-2147483648

        /**         *  根據(jù)尺寸和模式創(chuàng)建一個(gè)約束         */
        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.         * 從約束規(guī)范中獲取模式         */
        public static int getMode(int measureSpec) {            return (measureSpec & MODE_MASK);
        }        /**         * Extracts the size from the supplied measure specification.         * 從約束規(guī)范中獲取尺寸         */
        public static int getSize(int measureSpec) {            return (measureSpec & ~MODE_MASK);
        }
    }

SpecMode分為三類,具體的含義如下:

SpecMode對(duì)應(yīng)的布局參數(shù)輸出值說(shuō)明
UNSPECIFIED(未指明)無(wú)0父容器不對(duì)子控件做任何限制,要多大給多大,該情況一般用于系統(tǒng)內(nèi)部,表示一種測(cè)量狀態(tài)
EXACTLY(精確)match_parent/具體值(dp,px)1073741824已經(jīng)檢測(cè)出View所需要的大小,此時(shí)View的最終大小就是SpecSize的值,注意,如果是match_parent,就是說(shuō)明子控件把父容器剩下的尺寸都要了
AT_MOST(至多)wrap_content-2147483648父容器并不知道子控件到底需要多大,但是它指定了一個(gè)可用的尺寸SpecSize,子控件的大小不能超過(guò)該值,該情況下,不同view有不同的默認(rèn)實(shí)現(xiàn)方式,比如TextView就默認(rèn)包裹所有字符

MeasureSpec與LayoutParams的對(duì)應(yīng)關(guān)系

我們?cè)谇懊婢驼f(shuō)過(guò),一個(gè)View的尺寸受到父容器和本身LayoutParams雙重影響。在進(jìn)一步的討論之前,我們先來(lái)看一個(gè)ViewGroup中的一個(gè)重要方法。

```java

// #ViewGroup#getChildMeasureSpec
 public static int getChildMeasureSpec(int spec, int padding, int childDimension)

```

看方法名字,就可以知道是測(cè)量子view的MeasureSpec,并且該方法是在ViewGroup中調(diào)用的。
它的三個(gè)參數(shù)意思如下:

  1. int spec : ViewGroup自身的spec,HeightMeasureSpecWidthMeasureSpec。(如果站在child的角度來(lái)看,就是父容器)。

  2. int padding : 如果是width,則是ViewGroup左右Padding+子View左右Margin+widthUsed。
    如果是height,則是ViewGroup上下Padding+子View上下Margin+heightUsed。

  3. int childDimension : 就是 child的LayoutParmams(lp.widthlp.height)。

該方法具體實(shí)現(xiàn)如下:

ViewGroup#getChildMeasureSpec

 //此方法用來(lái)計(jì)算一個(gè)合適子視圖的尺寸大小 (HeightMeasureSpec或者WidthMeasureSpec)
 public static int getChildMeasureSpec(int spec, int padding, int childDimension) {        //得到父容器的size和mode
        int specMode = MeasureSpec.getMode(spec);        int specSize = MeasureSpec.getSize(spec);        //當(dāng)前ViewGroup剩余空間的大小。(站在子child的角度就是父容器剩余空間的大小).
        //我們可以理解成 parentSize-padding
        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;                //在ViewGroup的源碼定義中, LayoutParams.MATCH_PARENT = -1
            } else if (childDimension == LayoutParams.MATCH_PARENT) {                // Child wants to be our size. So be it.
                resultSize = size;
                resultMode = MeasureSpec.EXACTLY;                //在ViewGroup的源碼定義中, LayoutParams.MATCH_PARENT = -2
            } 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 = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                resultMode = MeasureSpec.UNSPECIFIED;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {                // Child wants to determine its own size.... find out how
                // big it should be
                resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                resultMode = MeasureSpec.UNSPECIFIED;
            }            break;
        }        //noinspection ResourceType
        return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
    }

該方法主要用來(lái)計(jì)算一個(gè)合適的子view的大小。里面是一系列的if else判斷,其實(shí)它的內(nèi)容,可以用一張表更加清晰的表現(xiàn)。

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

先記著這些內(nèi)容,因?yàn)樗谙旅娓徊降挠懻撝杏兄浅V匾淖饔谩?/p>

http://www.cnblogs.com/yaoxiaowen/p/7045931.html