引言
本章內(nèi)容較多,先養(yǎng)養(yǎng)眼
大家知道,自定義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):
- MeasureSpec封裝了父布局傳遞給子View的布局要求发钝。
- MeasureSpec可以表示寬和高
- 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的生成
好了,看到這個(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)?
為了理清這幾個(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)記部分)
我們?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在布局文件中采用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彰阴,那么父容器的父容器(以下簡稱“爺容器”)又該是什么情況呢?
- 爺容器的大小不可能是wrap_content(原理同情況1)
- 爺容器的大小不可能是某個(gè)具體的值辫封。 因?yàn)槿羝浯笮槟尘唧w值硝枉,那么其specMode應(yīng)該為MeasureSpec.EXACTLY;父容器的specMode也該為MeasureSpec.EXACTLY倦微。但是這里父容器的SpecMode為MeasureSpec.AT_MOST妻味,相互矛盾了。
- 爺容器的大小是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(四)