自定義View系列教程02--onMeasure源碼詳盡分析

引言

本章內(nèi)容較多,先養(yǎng)養(yǎng)眼


所侵權(quán)插勤,刪

大家知道,自定義View有三個(gè)重要的步驟:measure革骨,layout农尖,draw。而measure處于該鏈條的首端良哲,占據(jù)著極其重要的地位卤橄;然而對(duì)于measure的理解卻不是那么容易,許多問題都是一知半解臂外,比如:

  • 為什么父View影響到了子View的MeasureSpec的生成窟扑?
  • 為什么我們自定義一個(gè)View在布局時(shí)將其寬或者高指定為wrap_content但是其實(shí)際是match_parent的效果?
  • 子View的specMode和specSize的生成依據(jù)又是什么漏健?

這些問題以前一直困擾著我嚎货,我就去找資料看,搜了一大筐蔫浆,沮喪的發(fā)現(xiàn)這些文章大同小異:只舉個(gè)簡單的例子殖属,很少研究為什么;人云亦云瓦盛,文章里的內(nèi)容沒有去驗(yàn)證和深究就發(fā)出來了洗显;或者避重就輕直接把難點(diǎn)給繞過去了…….每次,看完這些文章就沒有勇氣去看layout和draw了原环,就算了挠唆;這可能就是《自定義View——從入門到放棄》的劇本吧≈雎穑看了那么多文章依舊不能解答原來的疑惑玄组;就像聽過了許多大道理依舊不過好這一生。連measure都沒有很好的理解又何談?wù)嬲睦斫鈒ayout和draw呢谒麦?要是能找到一篇文章能解開這些疑惑該有多好呀俄讹! 咦,等等绕德。要是一直沒有找到這么一篇文章那又怎么辦呢患膛?就真的不學(xué)習(xí)了?妙齡少婦郭大嬸不是說過么:每當(dāng)你在感嘆耻蛇,如果有這樣一個(gè)東西就好了的時(shí)候踪蹬,請(qǐng)注意胞此,其實(shí)這是你的機(jī)會(huì)。是啊延曙,機(jī)會(huì)來了豌鹤。嗯,我們來一起搞清楚這些問題枝缔,我們從源碼里來尋找答案布疙。

MeasureSpec基礎(chǔ)知識(shí)
系統(tǒng)顯示一個(gè)View,首先需要通過測(cè)量(measure)該View來知曉其長和寬從而確定顯示該View時(shí)需要多大的空間愿卸。在測(cè)量的過程中MeasureSpec貫穿全程灵临,發(fā)揮著不可或缺的作用。 所以趴荸,了解View的測(cè)量過程儒溉,最合適的切入點(diǎn)就是MeasureSpec。 我們先來瞅瞅官方文檔對(duì)于MeasureSpec 的介紹:

A MeasureSpec encapsulates the layout requirements passed from parent to child.Each MeasureSpec represents a requirement for either the width or the height.A MeasureSpec is comprised of a size and a mode.

請(qǐng)注意這段話所包含的重要信息點(diǎn):

  1. MeasureSpec封裝了父布局傳遞給子View的布局要求发钝。
  2. MeasureSpec可以表示寬和高
  3. MeasureSpec由size和mode組成

MeasureSpec通常翻譯為”測(cè)量規(guī)格”,它是一個(gè)32位的int數(shù)據(jù). 其中高2位代表SpecMode即某種測(cè)量模式顿涣,低30位為SpecSize代表在該模式下的規(guī)格大小. 可以通過如下方式分別獲取這兩個(gè)值:

//獲取specMode
int specMode = MeasureSpec.getMode(measureSpec)
//獲取SpecSize
int specSize = MeasureSpec.getSize(measureSpec)

當(dāng)然,也可以通過這兩個(gè)值生成新的MeasureSpec

int measureSpec=MeasureSpec.makeMeasureSpec(size, mode);

SpecMode一共有三種: MeasureSpec.EXACTLY 酝豪, MeasureSpec.AT_MOST 涛碑, MeasureSpec.UNSPECIFIED
嗯哼,它們已經(jīng)躺在這里了孵淘,我們來挨個(gè)瞅瞅蒲障,每個(gè)SpecMode是什么意思
MeasureSpec.EXACTLY
官方文檔的描述:

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.

MeasureSpec.EXACTLY模式表示:父容器已經(jīng)檢測(cè)出子View所需要的精確大小。 在該模式下瘫证,View的測(cè)量大小即為SpecSize揉阎。
MeasureSpec.AT_MOST
官方文檔的描述:

The child can be as large as it wants up to the specified size.

MeasureSpec.AT_MOST模式表示:父容器未能檢測(cè)出子View所需要的精確大小,但是指定了一個(gè)可用大小即specSize 在該模式下背捌,View的測(cè)量大小不能超過SpecSize毙籽。
MeasureSpec.UNSPECIFIED
官方文檔的描述:

The parent has not imposed any constraint on the child. It can be whatever size it wants.

父容器不對(duì)子View的大小做限制.
MeasureSpec.UNSPECIFIED這種模式一般用作Android系統(tǒng)內(nèi)部,或者ListView和ScrollView等滑動(dòng)控件载萌,在此不做討論惧财。
看完了這三個(gè)SpecMode的含義,我們?cè)購脑创a里看看它們是怎么形成的扭仁。
在ViewGroup中測(cè)量子View時(shí)會(huì)調(diào)用到measureChildWithMargins()方法,或者與之類似的方法厅翔。源碼如下:

/**
     * @param child
     * 子View
     * @param parentWidthMeasureSpec
     * 父容器(比如LinearLayout)的寬的MeasureSpec
     * @param widthUsed
     * 父容器(比如LinearLayout)在水平方向已經(jīng)占用的空間大小
     * @param parentHeightMeasureSpec
     * 父容器(比如LinearLayout)的高的MeasureSpec
     * @param heightUsed
     * 父容器(比如LinearLayout)在垂直方向已經(jīng)占用的空間大小
     */
    protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed,
                                           int parentHeightMeasureSpec, int heightUsed) {
15      final MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) child.getLayoutParams();
16      final int childWidthMeasureSpec =
17                getChildMeasureSpec(parentWidthMeasureSpec, mPaddingLeft + mPaddingRight +
18                                    lp.leftMargin + lp.rightMargin + widthUsed, lp.width);
19        final int childHeightMeasureSpec =
20                  getChildMeasureSpec(parentHeightMeasureSpec, mPaddingTop + mPaddingBottom +
21                                     lp.topMargin + lp.bottomMargin + heightUsed, lp.height);
22        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    }

請(qǐng)務(wù)必注意該方法的參數(shù)乖坠;明白這幾個(gè)參數(shù)的含義才能準(zhǔn)確理解方法的實(shí)現(xiàn)。
通過這些參數(shù)看出來一些端倪刀闷,該方法要測(cè)量子View傳進(jìn)來的參數(shù)卻包含了父容器的寬的MeasureSpec熊泵,父容器在水平方向已經(jīng)占用的空間大小仰迁,父容器的高的MeasureSpec,父容器在垂直方向已經(jīng)占用的空間大小等父View相關(guān)的信息顽分。這在一定程度體現(xiàn)了:父View影響著子View的MeasureSpec的生成徐许。

該方法主要有四步操作:
第一步: 得到子View的LayoutParams,請(qǐng)參見第15行代碼卒蘸。
第二步: 得到子View的寬的MeasureSpec雌隅,請(qǐng)參見第16-18行代碼。
第三步: 得到子View的高的MeasureSpec缸沃,請(qǐng)參見第19-21行代碼恰起。
第四步: 測(cè)量子View,請(qǐng)參見第22行代碼趾牧。

第一步检盼,沒啥好說的;第二步和第三步都調(diào)用到了getChildMeasureSpec( )翘单,在該方法內(nèi)部又做了哪些操作呢吨枉?

public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
2       int specMode = View.MeasureSpec.getMode(spec);
3       int specSize = View.MeasureSpec.getSize(spec);

5        int size = Math.max(0, specSize - padding);

        int resultSize = 0;
        int resultMode = 0;

10      switch (specMode) {
11          case View.MeasureSpec.EXACTLY:
                if (childDimension >= 0) {
                    resultSize = childDimension;
                    resultMode = View.MeasureSpec.EXACTLY;
                } else if (childDimension == LayoutParams.MATCH_PARENT) {
                    resultSize = size;
                    resultMode = View.MeasureSpec.EXACTLY;
                } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                    resultSize = size;
                    resultMode = View.MeasureSpec.AT_MOST;
21              }
22              break;

            case View.MeasureSpec.AT_MOST:
                if (childDimension >= 0) {
                    resultSize = childDimension;
                    resultMode = View.MeasureSpec.EXACTLY;
                } else if (childDimension == LayoutParams.MATCH_PARENT) {
                    resultSize = size;
                    resultMode = View.MeasureSpec.AT_MOST;
                } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                    resultSize = size;
                    resultMode = View.MeasureSpec.AT_MOST;
                }
                break;

            case View.MeasureSpec.UNSPECIFIED:
                if (childDimension >= 0) {
                    resultSize = childDimension;
                    resultMode = View.MeasureSpec.EXACTLY;
                } else if (childDimension == LayoutParams.MATCH_PARENT) {
                    resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                    resultMode = View.MeasureSpec.UNSPECIFIED;
                } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                    resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                    resultMode = View.MeasureSpec.UNSPECIFIED;
47              }
48              break;
49      }
50      return View.MeasureSpec.makeMeasureSpec(resultSize, resultMode);
51  }

請(qǐng)注意該方法的參數(shù):

spec
父容器(比如LinearLayout)的寬或高的MeasureSpec

padding
父容器(比如LinearLayout)在垂直方向或者水平方向已被占用的空間。
為什么這么說哄芜,它的依據(jù)在哪里貌亭?
請(qǐng)看在measureChildWithMargins()方法里調(diào)用getChildMeasureSpec()的地方,傳遞給getChildMeasureSpec()的第二個(gè)參數(shù)是如下構(gòu)成:
比如:mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin +widthUsed
其中:
mPaddingLeft和mPaddingRight表示父容器左右兩內(nèi)側(cè)的padding
lp.leftMargin和lp.rightMargin表示子View左右兩外側(cè)的margin
widthUsed水平方向已經(jīng)使用的空間大兄抑颉(如子View在水平方向的占用空間)

這四部分都不可以再利用起來布局子View属提。所以說這些值的和表示:父容器在水平方向已經(jīng)被占用的空間
同理:mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin +heightUsed,
表示: 父容器(比如LinearLayout)在垂直方向已被占用的空間.

childDimension
通過子View的LayoutParams獲取到的子View的寬或高

所以美尸,從getChildMeasureSpec()方法的第一個(gè)參數(shù)spec和第二個(gè)參數(shù)padding也可以看出:
父容器(如LinearLayout)的MeasureSpec和子View的LayoutParams共同決定了子View的MeasureSpec冤议!

明白了該方法的參數(shù),我們?cè)賮砜捶椒ǖ木唧w實(shí)現(xiàn)步驟师坎。

  • 第一步:得到父容器的specMode和specSize恕酸,請(qǐng)參見第2-3行代碼。
  • 第二步: 得到父容器在水平方向或垂直方向可用的最大空間值胯陋,請(qǐng)參見第5行代碼蕊温。
  • 第三步: 確定子View的specMode和specSize,請(qǐng)參見第10-50行代碼遏乔。

在上面的代碼塊中出現(xiàn)了一個(gè)很關(guān)鍵的switch語句义矛,該語句的判斷條件就是父View的specMode;在此根據(jù)父View的specMode的不同來決定子View的specMode和specSize.

情況1:

父容器的specMode為MeasureSpec.EXACTLY盟萨,請(qǐng)參見第11-22行代碼凉翻。
也請(qǐng)記住該先決條件,因?yàn)橐韵碌挠懻摱际腔诖苏归_的捻激。

1制轰、我們首先看到一個(gè)if判斷
if (childDimension >= 0)前计,或許看到這有點(diǎn)懵了:childDimension>=0是啥意思?難道還有小于0的情況垃杖?是的男杈,請(qǐng)注意兩個(gè)系統(tǒng)常量:
LayoutParams.MATCH_PARENT = -1 和 LayoutParams.WRAP_CONTENT = -2

所以在此處的代碼:
if (childDimension >= 0)
表示子View的寬或高不是match_parent,也不是wrap_content而是一個(gè)具體的數(shù)值调俘,比如100px伶棒。
那么:子View的size就是childDimension,子View的mode也為MeasureSpec.EXACTLY脉漏,即:

resultSize = childDimension; 
resultMode = MeasureSpec.EXACTLY;

2苞冯、看完這個(gè)if,我們來看第一個(gè)else if
else if (childDimension == LayoutParams.MATCH_PARENT)
表示子View的寬或高是LayoutParams.MATCH_PARENT侧巨。
那么:子View的size就是父容器在水平方向或垂直方向可用的最大空間值即size舅锄,子View的mode也為MeasureSpec.EXACTLY,即:

resultSize = size; 
resultMode = MeasureSpec.EXACTLY;

3司忱、我們來看第二個(gè)else if
else if (childDimension == LayoutParams.WRAP_CONTENT)
表示子View的寬或高是LayoutParams.WRAP_CONTENT皇忿。
那么:子View的size就是父容器在水平方向或垂直方向可用的最大空間值即size,子View的mode為MeasureSpec.AT_MOST坦仍,即:

resultSize = size; 
resultMode = MeasureSpec.AT_MOST;

情況2:

父容器的specMode為MeasureSpec.AT_MOST鳍烁,請(qǐng)參見第24-35行代碼。
也請(qǐng)記住該先決條件繁扎,因?yàn)橐韵碌挠懻摱际腔诖苏归_的幔荒。

1、還是先看這個(gè)if判斷
if (childDimension >= 0)
表示子View的寬或高不是match_parent梳玫,也不是wrap_content而是一個(gè)具體的數(shù)值爹梁,比如100px。
那么:子View的size就是childDimension提澎,子View的mode也為MeasureSpec.EXACTLY姚垃,即:

resultSize = childDimension; 
resultMode = MeasureSpec.EXACTLY;

2、繼續(xù)看第一個(gè)else if
else if (childDimension == LayoutParams.MATCH_PARENT)
表示子View的寬或高是LayoutParams.MATCH_PARENT盼忌。
那么:子View的size就是父容器在水平方向或垂直方向可用的最大空間值即size积糯,子View的mode也為MeasureSpec.AT_MOST,即:

resultSize = size; 
resultMode = MeasureSpec.AT_MOST;

3谦纱、接著看第二個(gè)else if
else if (childDimension == LayoutParams.WRAP_CONTENT)
表示子View的寬或高是LayoutParams.WRAP_CONTENT看成。
那么:子View的size就是父容器在水平方向或垂直方向可用的最大空間值即size,子View的mode也為MeasureSpec.AT_MOST

resultSize = size;
resultMode = MeasureSpec.AT_MOST;

情況3:

父容器的specMode為MeasureSpec.UNSPECIFIED跨嘉,請(qǐng)參見第37-48行代碼绍昂。
也請(qǐng)記住該先決條件,因?yàn)橐韵碌挠懻摱际腔诖苏归_的偿荷。

1窘游、還是先看這個(gè)if判斷
if (childDimension >= 0)
表示子View的寬或高不是match_parent,也不是wrap_content而是一個(gè)具體的數(shù)值跳纳,比如100px忍饰。
那么:子View的size就是childDimension,子View的mode也為MeasureSpec.EXACTLY寺庄,即:

resultSize = childDimension; 
resultMode = MeasureSpec.EXACTLY;

2艾蓝、繼續(xù)看第一個(gè)else if
else if (childDimension == LayoutParams.MATCH_PARENT)
表示子View的寬或高是LayoutParams.MATCH_PARENT。
那么:子View的size為0斗塘,子View的mode為MeasureSpec.UNSPECIFIED赢织,即:

resultSize = 0; 
resultMode = MeasureSpec.UNSPECIFIED;

3、接著看第二個(gè)else if
else if (childDimension == LayoutParams.WRAP_CONTENT)
表示子View的寬或高是LayoutParams.WRAP_CONTENT馍盟。
那么:子View的size為0于置,子View的mode為MeasureSpec.UNSPECIFIED,即:

resultSize = 0; 
resultMode = MeasureSpec.UNSPECIFIED;

至此贞岭,我們可以清楚地看到:
子View的MeasureSpec由其父容器的MeasureSpec和該子View本身的布局參數(shù)LayoutParams共同決定八毯。
在此經(jīng)過測(cè)量得出的子View的MeasureSpec是系統(tǒng)給出的一個(gè)期望值(參考值),我們也可摒棄系統(tǒng)的這個(gè)測(cè)量流程瞄桨,直接調(diào)用setMeasuredDimension( )設(shè)置子View的寬和高的測(cè)量值话速。

對(duì)于以上的分析可用表格來規(guī)整各一下MeasureSpec的生成


View的MeasureSpace創(chuàng)建規(guī)則

好了,看到這個(gè)圖芯侥,感覺清晰多了泊交。為了便于理解和記憶,我在此再用大白話再對(duì)該圖進(jìn)行詳細(xì)的描述:

在哪些具體的情況下子View的SpecMode為MeasureSpec.EXACTLY柱查? 在此廓俭,對(duì)各情況一一討論和分析:

第一種情況:

當(dāng)子View的LayoutParams的寬(高)采用具體的值(如100px)時(shí)且父容器的MeasureSpec為MeasureSpec.EXACTLY或者M(jìn)easureSpec.AT_MOST或者M(jìn)easureSpec.UNSPECIFIED時(shí):系統(tǒng)返回給該子View的specMode就為MeasureSpec.EXACTLY,系統(tǒng)返回給該子View的specSize就為子View自己指定的大小(childSize)物赶。

通俗地理解: 子View的LayoutParams的寬(高)采用具體的值(如100px)時(shí)白指,那么說明該子View的大小是非常明確的,明確到了令人發(fā)指的地址酵紫,都已經(jīng)到了用具體px值指定的地步了告嘲。那么此時(shí)不管父容器的specMode是什么,系統(tǒng)返回給該子View的specMode總是MeasureSpec.EXACTLY奖地,并且系統(tǒng)返回給該子View的specSize就是子View自己指定的大小(childSize)橄唬。

第二種情況:

當(dāng)子View的LayoutParams的寬(高)采用match_parent時(shí)并且父容器的MeasureSpec為MeasureSpec.EXACTLY時(shí):系統(tǒng)返回給該子View的specMode就為 MeasureSpec.EXACTLY,系統(tǒng)返回給該子View的specSize就為該父容器剩余空間的大小(parentLeftSize)参歹。

通俗地理解: 子View的LayoutParams的寬(高)采用match_parent并且父容器的MeasureSpec為MeasureSpec.EXACTLY仰楚。這時(shí)候說明子View的大小還是挺明確的:就是要和父容器一樣大,更加直白地說就是父容器要怎樣子View就要怎樣。所以僧界,如果父容器MeasureSpec為MeasureSpec.EXACTLY侨嘀,那么系統(tǒng)返回給該子View的specMode就為 MeasureSpec.EXACTLY和父容器一樣;系統(tǒng)返回給該子View的specSize就為該父容器剩余空間的大小(parentLeftSize),即為父容器的剩余大小.

在哪些具體的情況下子View的SpecMode為MeasureSpec.AT_MOST捂襟? 在此咬腕,對(duì)各情況一一討論和分析:

第一種情況:

當(dāng)子View的LayoutParams的寬(高)采用match_parent并且父容器的MeasureSpec為MeasureSpec.AT_MOST時(shí):系統(tǒng)返回給該子View的specMode就為MeasureSpec.AT_MOST,系統(tǒng)返回給該子View的specSize就為該父容器剩余空間的大小(parentLeftSize)

通俗地理解: 子View的LayoutParams的寬(高)采用match_parent并且父容器的MeasureSpec為MeasureSpec.AT_MOST葬荷。這時(shí)候說明子View的大小還是挺明確的:就是要和父容器一樣大涨共,直白地說就是父容器要怎樣子View就要怎樣。但是此時(shí)父容器的大小不是很明確其MeasureSpec為MeasureSpec.AT_MOST宠漩,那么系統(tǒng)返回給該子View的specMode就為MeasureSpec.AT_MOST和父容器一樣举反;系統(tǒng)返回給該子View的specSize就為該父容器剩余空間的大小(parentLeftSize),即為父容器的剩余大小.

第二種情況:

當(dāng)子View的LayoutParams的寬(高)采用wrap_content時(shí)并且父容器的MeasureSpec為MeasureSpec.EXACTLY時(shí):系統(tǒng)返回給該子View的specMode就為 MeasureSpec.AT_MOST扒吁,系統(tǒng)返回給該子View的specSize就為該父容器剩余空間的大小(parentLeftSize)

通俗地理解: 子View的LayoutParams的寬(高)采用wrap_content時(shí)說明這個(gè)子View的寬高不明確,要視content而定火鼻。這時(shí)如果父容器的MeasureSpec為MeasureSpec.EXACTLY即父容器是一個(gè)精確模式。這種情況概況起來簡單地說就是:子View大小是不確定的瘦陈,但父容器大小是確定的凝危,那么系統(tǒng)返回給該子View的specMode也就是不確定的即為MeasureSpec.AT_MOST,系統(tǒng)返回給該子View的specSize就為該父容器剩余空間的大小(parentLeftSize)

第三種情況:

當(dāng)子View的LayoutParams的寬(高)采用wrap_content時(shí)并且父容器的MeasureSpec為MeasureSpec.AT_MOST時(shí):系統(tǒng)返回給該子View的specMode就為MeasureSpec.AT_MOST晨逝,系統(tǒng)返回給該子View的specSize就為該父容器剩余空間的大小(parentLeftSize)
通俗地理解: 子View的LayoutParams的寬(高)采用wrap_content蛾默,即說明這個(gè)子View的寬高不明確,要視content而定。這個(gè)時(shí)候父容器的MeasureSpec為MeasureSpec.AT_MOST捉貌。這種情況概況起來簡單地說就是:子View的寬高是不確定的支鸡,父容器的寬高也是不確定的,那么系統(tǒng)返回給該子View的specMode也就是不確定的即為MeasureSpec.AT_MOST趁窃,系統(tǒng)返回給該子View的specSize就為該父容器剩余空間的大小(parentLeftSize)

在哪些具體的情況下子View的SpecMode為MeasureSpec.UNSPECIFIED牧挣?

前面也說了該模式在實(shí)際開發(fā)中極少用到,故在此不做討論醒陆。

剛才我們討論的是: measureChildWithMargins( )調(diào)用getChildMeasureSpec( )
除此以外還有一種常見的操作: measureChild( )調(diào)用getChildMeasureSpec( )
那么瀑构,measureChildWithMargins( )和measureChild( )有什么相同點(diǎn)和區(qū)別呢?

  • measureChildWithMargins( )和measureChild( )均用來測(cè)量子View的大小
  • 兩者在調(diào)用getChildMeasureSpec( )均要計(jì)算父View已占空間
  • 在measureChild( )計(jì)算父View所占空間為mPaddingLeft + mPaddingRight刨摩,即父容器左右兩側(cè)的padding值之和
  • measureChildWithMargins( )計(jì)算父View所占空間為mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin+ widthUsed寺晌。此處,除了父容器左右兩側(cè)的padding值之和還包括了子View左右的margin值之和( lp.leftMargin + lp.rightMargin)澡刹,因?yàn)檫@兩部分也是不能用來擺放子View的應(yīng)算作父View已經(jīng)占用的空間呻征。這點(diǎn)從方法名measureChildWithMargins也可以看出來它是考慮了子View的margin所占空間的。

其實(shí)罢浇,關(guān)于measureChildWithMargins()在源碼注釋中也說了:

Ask one of the children of this view to measure itself, taking into account both the MeasureSpec requirements for this view and its padding and margins.

好了陆赋,搞懂了MeasureSpec我們才正真地進(jìn)入到View的onMeasure()源碼分析沐祷。

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

onMeasure( )源碼流程如下:
(1) 在onMeasure調(diào)用setMeasuredDimension( )設(shè)置View的寬和高.
(2) 在setMeasuredDimension()中調(diào)用getDefaultSize()獲取View的寬和高.
(3) 在getDefaultSize()方法中又會(huì)調(diào)用到getSuggestedMinimumWidth()或者getSuggestedMinimumHeight()獲取到View寬和高的最小值.
即這一系列的方法調(diào)用順序?yàn)?


onMeasure()方法中調(diào)用順序

為了理清這幾個(gè)方法間的調(diào)用及其作用,在此按照倒序分析每個(gè)方法的源碼。
先來看getSuggestedMinimumWidth( )

//Returns the suggested minimum width that the view should use 
protected int getSuggestedMinimumWidth() {  
    return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());  
} 

該方法返回View的寬度的最小值MinimumWidth.
在此需要注意該View是否有背景.
(1) 若該View沒有背景攒岛。 那么該MinimumWidth為View本身的最小寬度即mMinWidth赖临。 有兩種方法可以設(shè)置該mMinWidth值: 第一種:XML布局文件中定義minWidth ;第二種:調(diào)用View的setMinimumWidth()方法為該值賦值阵子;

(2) 若該View有背景思杯。 那么該MinimumWidth為View本身最小寬度mMinWidth和View背景的最小寬度的最大值

getSuggestedMinimumHeight()方法與此處分析很類似,故不再贅述.
接下來看看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;  
     }  

該方法用于獲取View的寬或者高的大小。
該方法的第一個(gè)輸入?yún)?shù)size就是調(diào)用getSuggestedMinimumWidth()方法獲得的View的寬或高的最小值挠进。

從getDefaultSize()的源碼里的switch可看出該方法的返回值有兩種情況:
(1) measureSpec的specMode為MeasureSpec.UNSPECIFIED
在此情況下該方法的返回值就是View的寬或者高最小值.
該情況很少見,基本上可忽略

(2) measureSpec的specMode為MeasureSpec.AT_MOST或MeasureSpec.EXACTLY:
在此情況下getDefaultSize()的返回值就是該子View的measureSpec中的specSize。

除去第一種情況不考慮以外,可知:
在measure階段View的寬和高由其measureSpec中的specSize決定L懿帷领突!

看了這么久的源碼,我們終于搞清楚了這個(gè)問題案怯;但是剛剛舒展開的眉頭又皺起來了君旦。結(jié)合剛才的圖發(fā)現(xiàn)一個(gè)問題:在該圖的最后一行,如果子View在XML布局文件中對(duì)于大小的設(shè)置采用wrap_content嘲碱,那么不管父View的specMode是MeasureSpec.AT_MOST還是MeasureSpec.EXACTLY對(duì)于子View而言系統(tǒng)給它設(shè)置的specMode都是MeasureSpec.AT_MOST金砍,并且其大小都是parentLeftSize即父View目前剩余的可用空間。這時(shí)wrap_content就失去了原本的意義麦锯,變成了match_parent一樣了.

所以自定義View在重寫onMeasure()的過程中應(yīng)該手動(dòng)處理View的寬或高為wrap_content的情況恕稠。

至此,已經(jīng)看完了getSuggestedMinimumWidth()和getDefaultSize()
最后再來看setMeasuredDimension( )的源碼

protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {  
     mMeasuredWidth = measuredWidth;  
     mMeasuredHeight = measuredHeight;  
     mPrivateFlags |= MEASURED_DIMENSION_SET;  
}

經(jīng)過了前面的一系列操作扶欣,終于得到了View的寬高鹅巍。
在此調(diào)用setMeasuredDimension( )設(shè)置View的寬和高的測(cè)量值


好了,關(guān)于View的onMeasure( )的源碼及其調(diào)用流程都已經(jīng)分析完了料祠。 但是骆捧,剛才還遺留了一個(gè)問題: 自定義View在重寫onMeasure()的過程中要處理View的寬或高為wrap_content的情況(請(qǐng)參見下圖中的綠色標(biāo)記部分)
View的MeasureSpec創(chuàng)建規(guī)則

我們?cè)撛趺刺幚砟兀?br> 第一種情況:
如果在xml布局中View的寬和高均用wrap_content.那么需要設(shè)置
View的寬和高為mWidth和mHeight.
第二種情況:
如果在xml布局中View的寬或高其中一個(gè)為wrap_content,那么就將該值設(shè)置為默認(rèn)的寬或高,另外的一個(gè)值采用系統(tǒng)測(cè)量的specSize即可.

具體的實(shí)現(xiàn)可以這樣做:

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
2   super.onMeasure(widthMeasureSpec , heightMeasureSpec);  

4   int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);  
5   int widthSpceSize = MeasureSpec.getSize(widthMeasureSpec);  

    int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);  
    int heightSpceSize = MeasureSpec.getSize(heightMeasureSpec);  

    if(widthSpecMode==MeasureSpec.AT_MOST&&heightSpecMode==MeasureSpec.AT_MOST){  
        setMeasuredDimension(mWidth, mHeight);  
    }else if(widthSpecMode==MeasureSpec.AT_MOST){  
        setMeasuredDimension(mWidth, heightSpceSize);  
    }else if(heightSpecMode==MeasureSpec.AT_MOST){  
15      setMeasuredDimension(widthSpceSize, mHeight);  
16  }  

 }  

該部分的處理主要有兩步

第一步:
調(diào)用super.onMeasure(),請(qǐng)參見第2行代碼

第二步:
處理子View的大小為wrap_content的情況髓绽,請(qǐng)參見第4-16行代碼敛苇。
此處涉及到的mWidth和mHeight均為一個(gè)默認(rèn)值;應(yīng)根據(jù)具體情況而設(shè)值顺呕。
其實(shí)枫攀,Andorid系統(tǒng)的控件比如TextView等也在onMeasure()中對(duì)其大小為wrap_content這一情況作了特殊的處理。

請(qǐng)注意在第二步的代碼中用的判斷條件:
widthSpecMode==MeasureSpec.AT_MOST或者h(yuǎn)eightSpecMode==MeasureSpec.AT_MOST或者兼而有之;總之,這里的著眼點(diǎn)是模式MeasureSpec.AT_MOST塘匣。
看到這里再聯(lián)想到下圖就有一個(gè)疑問了(請(qǐng)參見下圖中的紅色標(biāo)記部分):


View的MeasureSpec創(chuàng)建規(guī)則

如果子View在布局文件中采用match_parent脓豪,并且父容器的SpecMode為MeasureSpec.AT_MOST, 那么此時(shí)該子View的SpecMode也為MeasureSpec.AT_MOST且其View的大小為parentLeftSize。

既然此時(shí)該子View的SpecMode也為MeasureSpec.AT_MOST那么當(dāng)執(zhí)行到onMeasure()時(shí)按照我們的判斷邏輯,它的寬或者高至少有一個(gè)會(huì)被設(shè)置成默認(rèn)值(mWidth和mHeight)忌卤。說白了扫夜,本來是個(gè)match_parent,卻被設(shè)置成了具體指的默認(rèn)值。

看到這里好像覺得也沒啥不對(duì)笤闯,但是不符合常理!!!到底是哪里錯(cuò)了堕阔??颗味? 我們這么想: 在什么情況下父容器的SpecMode為MeasureSpec.AT_MOST超陆? 這個(gè)問題不難回答,共有兩種情況:
情況1: 當(dāng)父容器的大小為wrap_content時(shí)系統(tǒng)給父容器的SpecMode為MeasureSpec.AT_MOST.
情況2: 當(dāng)父容器的大小為match_parent時(shí)系統(tǒng)給父容器的SpecMode為MeasureSpec.AT_MOST.

回答了這個(gè)問題就以此答案為基礎(chǔ)繼續(xù)討論浦马。 剛才的問題就可以描述為以下兩種情況:
情況1: 當(dāng)父容器大小為wrap_content且其specMode為MeasureSpec.AT_MOST时呀,子View大小為match_parent。 也就是說:子View想和父容器一樣大但父容器的大小又設(shè)定為包裹內(nèi)容大小即wrap_content晶默。那么谨娜,到底誰決定誰呢?誰也不能決定誰磺陡!父View和子View這父子倆就這么耗上了趴梢。 所以,該情況是理論上存在的但在實(shí)際情況中是很不合理甚至錯(cuò)誤的币他,當(dāng)然也是不可取的坞靶。

情況2: 當(dāng)父容器大小為match_parent且其SpecMode為MeasureSpec.AT_MOST,子View大小為match_parent蝴悉。
既然父容器大小為match_parent且其SpecMode為MeasureSpec.AT_MOST彰阴,那么父容器的父容器(以下簡稱“爺容器”)又該是什么情況呢?

  1. 爺容器的大小不可能是wrap_content(原理同情況1)
  2. 爺容器的大小不可能是某個(gè)具體的值辫封。 因?yàn)槿羝浯笮槟尘唧w值硝枉,那么其specMode應(yīng)該為MeasureSpec.EXACTLY;父容器的specMode也該為MeasureSpec.EXACTLY倦微。但是這里父容器的SpecMode為MeasureSpec.AT_MOST妻味,相互矛盾了。
  3. 爺容器的大小是match_parent欣福;那么其SpecMode有兩種情況:MeasureSpec.EXACTLY或者M(jìn)easureSpec.AT_MOST责球。 在此,為了便于理清思路拓劝,繼續(xù)分情況來討論 第一種情況: 爺容器的大小是match_parent雏逾,SpecMode為MeasureSpec.EXACTLY,且父容器此時(shí)大小為match_parent郑临;那么父容器的SpecMode應(yīng)該為MeasureSpec.EXACTLY栖博;但是這里父容器的SpecMode為MeasureSpec.AT_MOST。兩者是矛盾的厢洞。所以不會(huì)出現(xiàn)這個(gè)情況仇让。 第二種情況: 爺容器的大小是match_parent典奉,SpecMode為MeasureSpec.AT_MOST,且父容器此時(shí)大小為match_parent丧叽,那么父容器的SpecMode可以為MeasureSpec.AT_MOST卫玖。這是唯一可能存在的情況。 試著將這種情況抽取出來踊淳,就陷入到一個(gè)循環(huán):子View大小是match_parent其SpecMode為MeasureSpec.AT_MOST假瞬;父View的大小也是match_parent其SpecMode也為MeasureSpec.AT_MOST,爺容器亦如此……..直到根View根為止迂尝。但是根View的大小如果為match_parent脱茉,那么其SpecMode必為MeasureSpec.EXACTLY。所以這種情況也是矛盾的雹舀,也不會(huì)出現(xiàn)芦劣。
    至此,綜上所述说榆,我們發(fā)現(xiàn):子View在布局文件中采用match_parent,并且父容器的SpecMode為MeasureSpec.AT_MOST,那么此時(shí)該子View的SpecMode也為MeasureSpec.AT_MOST且其View的大小為parentLeftSize這種情況本身(圖中紅色標(biāo)記部分)就是不合理的寸认,不可取的签财。將這個(gè)問題再次抽取就可以簡化為情況1,殊途同歸偏塞。
    以此類推:
    (1) 不可能出現(xiàn)根View的大小為wrap_content但它的一個(gè)子View大小為match_parent唱蒸。
    (2) 從根到這個(gè)子View的父容器都是wrap_content,而子View的大小為match_parent灸叼。這個(gè)極端情況也是不會(huì)的神汹,可見情況1的分析.
    (3)從根到這個(gè)子View的父容器都是wrap_content,而子View大小也為wrap_content古今。這是個(gè)正常情況也正是我們改良后的onMeasure()來專門處理的子View大小為wrap_content的情況屁魏。

剛分析完了View的onMeasure()源碼,現(xiàn)在接著看ViewGroup的measure階段的實(shí)現(xiàn)

public abstract class ViewGroup extends View implements ViewParent,ViewManager{ }

首先捉腥,請(qǐng)注意ViewGroup是一個(gè)抽象類氓拼,它沒有重寫View的onMeasure( )但它提供了measureChildren( )測(cè)量所有的子View。在measureChildren()方法中調(diào)用measureChild( )測(cè)量每個(gè)子View抵碟,在該方法內(nèi)又會(huì)調(diào)用到child.measure( )方法桃漾,這又回到了前面熟悉的流程。 即ViewGroup中測(cè)量子View的流程: measureChildren( )—>measureChild( )—>child.measure( )
既然ViewGroup繼承自View而且它是一個(gè)抽象類拟逮,那么ViewGroup的子類就應(yīng)該根據(jù)自身的要求和特點(diǎn)重寫onMeasure( )方法撬统。 那么這些ViewGroup的子類會(huì)怎么測(cè)量子View和確定自身的大小呢? 假若ViewGroup知道了每個(gè)子View的大小敦迄,將它們累加起來是不是就知道了自身的大小呢恋追?
順著這個(gè)猜想凭迹,我們選擇ViewGroup的子類LinearLayout瞅瞅,就從它的onMeasure( )開始看吧

 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (mOrientation == VERTICAL) {
            measureVertical(widthMeasureSpec, heightMeasureSpec);
        } else {
            measureHorizontal(widthMeasureSpec, heightMeasureSpec);
        }
    }

嗯哼几于,代碼里分了水平線性和垂直線性這兩種情況進(jìn)行測(cè)量蕊苗,類似的邏輯在其onLayout( )階段也會(huì)看到的。我們就選measureVertical( )繼續(xù)往下看

void measureVertical(int widthMeasureSpec, int heightMeasureSpec) {
        mTotalLength = 0;
        int maxWidth = 0;
        int childState = 0;
        int alternativeMaxWidth = 0;
        int weightedMaxWidth = 0;
        boolean allFillParent = true;
        float totalWeight = 0;

        final int count = getVirtualChildCount();

        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        final int heightMode = MeasureSpec.getMode(heightMeasureSpec);

        boolean matchWidth = false;
        boolean skippedMeasure = false;

        final int baselineChildIndex = mBaselineAlignedChildIndex;        
        final boolean useLargestChild = mUseLargestChild;

        int largestChildHeight = Integer.MIN_VALUE;

        for (int i = 0; i < count; ++i) {
            final View child = getVirtualChildAt(i);

            if (child == null) {
                mTotalLength += measureNullChild(i);
                continue;
            }

            if (child.getVisibility() == View.GONE) {
               i += getChildrenSkipCount(child, i);
               continue;
            }

            if (hasDividerBeforeChildAt(i)) {
                mTotalLength += mDividerHeight;
            }

            LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();

            totalWeight += lp.weight;

            if (heightMode == MeasureSpec.EXACTLY && lp.height == 0 && lp.weight > 0) {
                final int totalLength = mTotalLength;
                mTotalLength = Math.max(totalLength, totalLength + lp.topMargin + lp.bottomMargin);
                skippedMeasure = true;
            } else {
                int oldHeight = Integer.MIN_VALUE;

                if (lp.height == 0 && lp.weight > 0) {

                    oldHeight = 0;
                    lp.height = LayoutParams.WRAP_CONTENT;
                }


                measureChildBeforeLayout(
                       child, i, widthMeasureSpec, 0, heightMeasureSpec,
                       totalWeight == 0 ? mTotalLength : 0);

                if (oldHeight != Integer.MIN_VALUE) {
                   lp.height = oldHeight;
                }

                final int childHeight = child.getMeasuredHeight();
               final int totalLength = mTotalLength;
                mTotalLength = Math.max(totalLength, totalLength + childHeight + lp.topMargin +
                       lp.bottomMargin + getNextLocationOffset(child));

                if (useLargestChild) {
                    largestChildHeight = Math.max(childHeight, largestChildHeight);
                }
            }


            if ((baselineChildIndex >= 0) && (baselineChildIndex == i + 1)) {
               mBaselineChildTop = mTotalLength;
            }
            if (i < baselineChildIndex && lp.weight > 0) {
                throw new RuntimeException("Exception");
            }

            boolean matchWidthLocally = false;
            if (widthMode != MeasureSpec.EXACTLY && lp.width == LayoutParams.MATCH_PARENT) {
                matchWidth = true;
                matchWidthLocally = true;
            }

            final int margin = lp.leftMargin + lp.rightMargin;
            final int measuredWidth = child.getMeasuredWidth() + margin;
            maxWidth = Math.max(maxWidth, measuredWidth);
            childState = combineMeasuredStates(childState, child.getMeasuredState());

            allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT;
            if (lp.weight > 0) {
                weightedMaxWidth = Math.max(weightedMaxWidth,
                        matchWidthLocally ? margin : measuredWidth);
            } else {
                alternativeMaxWidth = Math.max(alternativeMaxWidth,
                        matchWidthLocally ? margin : measuredWidth);
            }

            i += getChildrenSkipCount(child, i);
        }

        if (mTotalLength > 0 && hasDividerBeforeChildAt(count)) {
            mTotalLength += mDividerHeight;
        }

        if (useLargestChild &&
                (heightMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.UNSPECIFIED)) {
            mTotalLength = 0;

115         for (int i = 0; i < count; ++i) {
116             final View child = getVirtualChildAt(i);
117
118             if (child == null) {
                    mTotalLength += measureNullChild(i);
                    continue;
                }

                if (child.getVisibility() == GONE) {
                    i += getChildrenSkipCount(child, i);
                    continue;
                }

                final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
                        child.getLayoutParams();
130             final int totalLength = mTotalLength;
131             mTotalLength = Math.max(totalLength, totalLength + largestChildHeight +
132                     lp.topMargin + lp.bottomMargin + getNextLocationOffset(child));
133         }
        }

        mTotalLength += mPaddingTop + mPaddingBottom;

        int heightSize = mTotalLength;

        heightSize = Math.max(heightSize, getSuggestedMinimumHeight());

        int heightSizeAndState = resolveSizeAndState(heightSize, heightMeasureSpec, 0);
        heightSize = heightSizeAndState & MEASURED_SIZE_MASK;

        int delta = heightSize - mTotalLength;
        if (skippedMeasure || delta != 0 && totalWeight > 0.0f) {
            float weightSum = mWeightSum > 0.0f ? mWeightSum : totalWeight;

            mTotalLength = 0;

            for (int i = 0; i < count; ++i) {
                final View child = getVirtualChildAt(i);

                if (child.getVisibility() == View.GONE) {
                    continue;
                }

                LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();

                float childExtra = lp.weight;
                if (childExtra > 0) {
                    int share = (int) (childExtra * delta / weightSum);
                    weightSum -= childExtra;
                    delta -= share;

                    final int childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
                            mPaddingLeft + mPaddingRight +
                                    lp.leftMargin + lp.rightMargin, lp.width);


                    if ((lp.height != 0) || (heightMode != MeasureSpec.EXACTLY)) {
                        int childHeight = child.getMeasuredHeight() + share;
                        if (childHeight < 0) {
                            childHeight = 0;
                        }

                        child.measure(childWidthMeasureSpec,
                                MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY));
                    } else {
                        child.measure(childWidthMeasureSpec,
                                MeasureSpec.makeMeasureSpec(share > 0 ? share : 0,
                                        MeasureSpec.EXACTLY));
                    }

                    childState = combineMeasuredStates(childState, child.getMeasuredState()
                            & (MEASURED_STATE_MASK>>MEASURED_HEIGHT_STATE_SHIFT));
                }

                final int margin =  lp.leftMargin + lp.rightMargin;
                final int measuredWidth = child.getMeasuredWidth() + margin;
                maxWidth = Math.max(maxWidth, measuredWidth);

                boolean matchWidthLocally = widthMode != MeasureSpec.EXACTLY &&
                        lp.width == LayoutParams.MATCH_PARENT;

                alternativeMaxWidth = Math.max(alternativeMaxWidth,
                        matchWidthLocally ? margin : measuredWidth);

                allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT;

                final int totalLength = mTotalLength;
                mTotalLength = Math.max(totalLength, totalLength + child.getMeasuredHeight() +
                        lp.topMargin + lp.bottomMargin + getNextLocationOffset(child));
            }

            mTotalLength += mPaddingTop + mPaddingBottom;
        } else {
            alternativeMaxWidth = Math.max(alternativeMaxWidth,
                                           weightedMaxWidth);

            if (useLargestChild && heightMode != MeasureSpec.EXACTLY) {
                for (int i = 0; i < count; i++) {
                    final View child = getVirtualChildAt(i);

                    if (child == null || child.getVisibility() == View.GONE) {
                        continue;
                    }

                    final LinearLayout.LayoutParams lp =
                            (LinearLayout.LayoutParams) child.getLayoutParams();

                    float childExtra = lp.weight;
                    if (childExtra > 0) {
                        child.measure(
                                MeasureSpec.makeMeasureSpec(child.getMeasuredWidth(),
                                        MeasureSpec.EXACTLY),
                                MeasureSpec.makeMeasureSpec(largestChildHeight,
                                        MeasureSpec.EXACTLY));
                    }
                }
            }
        }

        if (!allFillParent && widthMode != MeasureSpec.EXACTLY) {
            maxWidth = alternativeMaxWidth;
        }

238     maxWidth += mPaddingLeft + mPaddingRight;
239     maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
240
241     setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
                heightSizeAndState);

        if (matchWidth) {
            forceUniformWidth(count, heightMeasureSpec);
        }
    }

這段代碼的主要操作:

遍歷每個(gè)子View,并對(duì)每個(gè)子View調(diào)用measureChildBeforeLayout()殉了,請(qǐng)參見代碼第115-133行
在measureChildBeforeLayout()方法內(nèi)又會(huì)調(diào)用measureChildWithMargins()從而測(cè)量每個(gè)子View的大小捺萌。在該過程中mTotalLength保存了LinearLayout的高度,所以每當(dāng)測(cè)量完一個(gè)子View該值都會(huì)發(fā)生變化瞧柔。
設(shè)置LinearLayout的大小,請(qǐng)參見代碼第241
以上就是LinearLayout的onMeasure( )的簡要分析睦裳。
其余的ViewGroup的子類均有自己各不相同的measure操作造锅,具體實(shí)現(xiàn)請(qǐng)參考對(duì)應(yīng)的源碼。

終于廉邑,分析完了和Measure有關(guān)的主要代碼哥蔚。
代碼稍微有點(diǎn)多,所以就有點(diǎn)小小的犯暈了蛛蒙。
再回過頭看剛才討論過的兩個(gè)方法:

以下為measureChildWithMargins

/**
     * @param child The child to measure
     * @param parentWidthMeasureSpec The width requirements for this view
     * @param widthUsed Extra space that has been used up by the parent
     *        horizontally (possibly by other children of the parent)
     * @param parentHeightMeasureSpec The height requirements for this view
     * @param heightUsed Extra space that has been used up by the parent
     *        vertically (possibly by other children of the parent)
     */
    protected void measureChildWithMargins(View child,
            int parentWidthMeasureSpec, int widthUsed,
            int parentHeightMeasureSpec, int heightUsed) {

    }

對(duì)于參數(shù)parentWidthMeasureSpec和parentHeightMeasureSpec注釋的描述是:

The width(height) requirements for this view
父容器對(duì)子View寬(高)的要求(或者說是期望值)糙箍。

以下為getChildMeasureSpec

/**
     *
     * @param spec The requirements for this view
     * @param padding The padding of this view for the current dimension and margins, if applicable
     * @param childDimension How big the child wants to be in the current dimension
     * @return a MeasureSpec integer for the child
     */
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {

    }

注釋對(duì)于第一個(gè)參數(shù)spec的描述也是類似的:

The requirements for this view
咦,為什么我們?cè)诮庾x這兩個(gè)方法的時(shí)候說:
parentWidthMeasureSpec和parentHeightMeasureSpec和spec這三個(gè)值表示寬或者高的MeasureSpec呢牵祟?

我們的表述和注釋的說明怎么不一樣呢深夯?
難道我們理解錯(cuò)了?非也诺苹,非也咕晋。

子View的MeasureSpec是由父View的MeasureSpec及子View自身的LayoutParam共同決定的。
關(guān)于“子View自身的LayoutParam”好理解收奔,就是子View的寬或高等條件
那“父View的MeasureSpec”又體現(xiàn)在哪里呢掌呜?
當(dāng)然就是這里出現(xiàn)的parentWidthMeasureSpec和parentHeightMeasureSpec以及spec。只不過文檔說得委婉些“對(duì)于子View的要求(期望)”筹淫。
其實(shí)站辉,我個(gè)人覺得理解成為“要求”更好一些,就是父View對(duì)子View的要求损姜。比如饰剥,在絕大多數(shù)情況下父View要求子View的大小不能超過其大小,這就是一種要求摧阅。而父View的大小就封裝在父View的MeasureSpec里的汰蓉。
所以,可以從這個(gè)角度來體會(huì)文檔的描述棒卷。
嗯哼顾孽,終于看完了measure的過程祝钢。
當(dāng)理解了MeasureSpec和measure的原理,我們才能更好的理解layout和draw從而掌握自定義View的流程若厚。


who is the next one拦英? ——> layout

原文地址:http://blog.csdn.net/lfdfhl/article/details/51347818

自定義View系列教程01--常用工具介紹
自定義View系列教程02--onMeasure源碼詳盡分析
自定義View系列教程03--onLayout源碼詳盡分析
自定義View系列教程04--Draw源碼分析及其實(shí)踐
自定義View系列教程05–示例分析
自定義View系列教程06–詳解View的Touch事件處理
自定義View系列教程07–詳解ViewGroup分發(fā)Touch事件
自定義View系列教程08–滑動(dòng)沖突的產(chǎn)生及其處理

參考文章
Activity中setContentView剖析
Android走進(jìn)Framework之AppCompatActivity.setContentView

Android LayoutInflater原理分析,帶你一步步深入了解View(一)
Android視圖繪制流程完全解析测秸,帶你一步步深入了解View(二)
Android視圖狀態(tài)及重繪流程分析疤估,帶你一步步深入了解View(三)
Android自定義View的實(shí)現(xiàn)方法,帶你一步步深入了解View(四)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末霎冯,一起剝皮案震驚了整個(gè)濱河市铃拇,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌沈撞,老刑警劉巖慷荔,帶你破解...
    沈念sama閱讀 223,126評(píng)論 6 520
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異缠俺,居然都是意外死亡显晶,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,421評(píng)論 3 400
  • 文/潘曉璐 我一進(jìn)店門壹士,熙熙樓的掌柜王于貴愁眉苦臉地迎上來吧碾,“玉大人,你說我怎么就攤上這事墓卦。” “怎么了户敬?”我有些...
    開封第一講書人閱讀 169,941評(píng)論 0 366
  • 文/不壞的土叔 我叫張陵落剪,是天一觀的道長。 經(jīng)常有香客問我尿庐,道長忠怖,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,294評(píng)論 1 300
  • 正文 為了忘掉前任抄瑟,我火速辦了婚禮凡泣,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘皮假。我一直安慰自己鞋拟,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,295評(píng)論 6 398
  • 文/花漫 我一把揭開白布惹资。 她就那樣靜靜地躺著贺纲,像睡著了一般。 火紅的嫁衣襯著肌膚如雪褪测。 梳的紋絲不亂的頭發(fā)上猴誊,一...
    開封第一講書人閱讀 52,874評(píng)論 1 314
  • 那天潦刃,我揣著相機(jī)與錄音,去河邊找鬼懈叹。 笑死乖杠,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的澄成。 我是一名探鬼主播胧洒,決...
    沈念sama閱讀 41,285評(píng)論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼环揽!你這毒婦竟也來了略荡?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 40,249評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤歉胶,失蹤者是張志新(化名)和其女友劉穎汛兜,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體通今,經(jīng)...
    沈念sama閱讀 46,760評(píng)論 1 321
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡粥谬,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,840評(píng)論 3 343
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了辫塌。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片漏策。...
    茶點(diǎn)故事閱讀 40,973評(píng)論 1 354
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖臼氨,靈堂內(nèi)的尸體忽然破棺而出掺喻,到底是詐尸還是另有隱情,我是刑警寧澤储矩,帶...
    沈念sama閱讀 36,631評(píng)論 5 351
  • 正文 年R本政府宣布感耙,位于F島的核電站,受9級(jí)特大地震影響持隧,放射性物質(zhì)發(fā)生泄漏即硼。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,315評(píng)論 3 336
  • 文/蒙蒙 一屡拨、第九天 我趴在偏房一處隱蔽的房頂上張望只酥。 院中可真熱鬧,春花似錦呀狼、人聲如沸裂允。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,797評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽叫胖。三九已至,卻和暖如春她奥,著一層夾襖步出監(jiān)牢的瞬間瓮增,已是汗流浹背怎棱。 一陣腳步聲響...
    開封第一講書人閱讀 33,926評(píng)論 1 275
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留绷跑,地道東北人拳恋。 一個(gè)月前我還...
    沈念sama閱讀 49,431評(píng)論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像砸捏,于是被迫代替她去往敵國和親谬运。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,982評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容