簡要
??今天來聊聊MeasureSpec,記得剛接觸的也感覺很難理解宇驾,知其然不知其所以然涮阔。MeasureSpec其實(shí)在面試中還經(jīng)常會被問到,如果沒有真正去理解它秩贰,不論是后續(xù)的開發(fā)或者面試中霹俺,難免會成為絆腳石。遂今天在此聊聊這個毒费,可能以下文中理解有所偏頗丙唧。如果有什么不到之處或者看完還有不太理解的地方,可以在文章后面寫下你的評論一起去探討觅玻。
??一說到MeasureSpec想际,大家本能反應(yīng)是其是由高兩位的mode和低30位的size組成的32位的一個int值,但要是再深入問一些可能回答不出來溪厘,例如:MeasureSpec為什么由mode+size的方式組成沼琉?MeasureSpec的值跟什么有關(guān)?或者父View與子View的MeasureSpec值的關(guān)系桩匪?自定義Viewd的onMeasure()方法要不要重寫?你還在為這些面試中經(jīng)常問的問題為難到嗎友鼻?相信看完下文傻昙,心里多多少少都會有個答案。
組成
??其實(shí)MeasureSpec是View的一個內(nèi)部類彩扔,真正的身份就是幫助View完成測量功能妆档。MeasureSpec類中最主要的部分由5個變量和3個方法組成,接下來根據(jù)原碼中的代碼慢慢一層層剖析虫碉。
變量
五個成員變量:
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);
}
}
@MeasureSpecMode
public static int getMode(int measureSpec) {
//noinspection ResourceType
return (measureSpec & MODE_MASK);
}
public static int getSize(int measureSpec) {
return (measureSpec & ~MODE_MASK);
}
舉例:
位運(yùn)算符號:
<< :左移位運(yùn)算 符號左邊是值贾惦,符號右邊是左移的位數(shù)
& :與運(yùn)算 相同位的值同為1則為,否為0
~ :優(yōu)先級比算數(shù)運(yùn)算符敦捧、關(guān)系運(yùn)算符须板、邏輯運(yùn)算符和其他運(yùn)算符都高
解釋
- MeasureSpec由mode+size的形式組合而成32位int值的好處是減少變量的創(chuàng)建
- MODE_MASK、UNSPECIFIED兢卵、EXACTLY 习瑰、AT_MOST 四個變量均左移30位,剛好高兩位組成2*2排列組合秽荤,由位運(yùn)算特性mode=MODE_MASK&mode
- makeMeasureSpec()方法目的就是通過mode和size組成MesureSpec的值甜奄,if條件判斷SDK版本是否低于17柠横,但是結(jié)果是相同的,只是高版本采用位運(yùn)算的方式獲取课兄。
- getMode()方法是獲取measureSpec的mode值牍氛,由于MODE_MASK低30位全是0,因此MODE_MASK&measureSpec計算的結(jié)果低30位為0烟阐,又因?yàn)閙easureSpec的高兩位為mode值搬俊,又因?yàn)镸ODE_MASK的高兩位全為1,因此MODE_MASK&measureSpec高兩位的值為mode高兩位的值曲饱,再加上低30位的0悠抹,因此MODE_MASK&measureSpec的結(jié)果就是mode值。
- getSize()方法是獲取measureSpec的size值扩淀,由于~ 位運(yùn)算優(yōu)先級高于&運(yùn)算脂倦,因此先~運(yùn)算再&運(yùn)算∑ň螅~MODE_MASK運(yùn)算之后高兩位為0奈嘿,低30位為1∈る~MODE_MASK&measureSpec的高兩位只能為0勺卢,又因?yàn)閙easureSpec的低30位size值,因此~MODE_MASK&measureSpec的低30位為size值象对。
來源和決定因素
??上面的組成部分講解了MeasureSpec內(nèi)部結(jié)構(gòu)黑忱,其中mode與size參數(shù)的意義和如何獲取。接下來說一說其如何幫助測量View的勒魔。我們都知道整個View體系是樹狀結(jié)構(gòu)的甫煞,測量是由上至下的過程,View的測量功能由onMeasure()和measure()方法完成冠绢,其中onMeasure()屬于回調(diào)方法抚吠,其結(jié)果再一層層回溯給頂層View。其中開發(fā)中我們只需要關(guān)注onMeasure()方法弟胀。onMeasure()方法中最重要的參數(shù)就是MeasureSpec值楷力,接下來看一下自定義View的onMeasure()方法。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int specMode = MeasureSpec.getMode(widthMeasureSpec);
switch (specMode) {
case MeasureSpec.EXACTLY:
Log.e("TAG", "ChildView:MeasureSpec.EXACTLY");
break;
case MeasureSpec.AT_MOST:
Log.e("TAG", "ChildView:MeasureSpec.AT_MOST");
break;
case MeasureSpec.UNSPECIFIED:
Log.e("TAG", "ChildView:MeasureSpec.UNSPECIFIED");
break;
}
}
說明:
本文以下內(nèi)容均只針對寬方向上來說
- 源頭:widthMeasureSpec值是由其父類傳遞過來的
- 值 :widthMeasureSpec(自身)=super.widthMeasureSpec(父)+this.LayoutParams(自身)結(jié)果得來的
- LayoutParams是ViewGroup的靜態(tài)內(nèi)部類孵户,由widthparam+widthparam組成萧朝,LayoutParams的類型是其父類的類型,值是自身的延届,常用的ViewGroup都重寫剪勿,新增Gravity方位值。其值分為三類方庭,MATCH_PARENT(-1)厕吉、WRAP_CONTENT(-2)酱固、具體的dp值,后面具體的dp值用dp代替
上圖說明:
1头朱、上圖是自定義View沒重寫onMeasure()的情況运悲,謹(jǐn)記!O钆ァ班眯!
2、childView括號中的三個值從左到右為:LayoutParams的值烁巫、MeasureSpec的mode值(已經(jīng)確定的)署隘、測量過后的自身size值(此時稱為measuredWidth)
3、正常情況下UNSPECIFIED模式用不到亚隙,所以自定義View的時候可以不做處理
4磁餐、getMeasuredWidth()≠getWidth(),getMeasuredWidth()在測量之后就有值了阿弃,但是getWidth()是沒有值的
5诊霹、只有自身mode為EXACTLY時,測量的size值為自己設(shè)置的渣淳,mode為AT_MOST的時候size全是父類的值
總結(jié)
??這里做一下總結(jié)脾还,其中有兩部分組成,第一部分是內(nèi)部的組成關(guān)系入愧,組成關(guān)系中最主要的是幾個位運(yùn)算和幾個參數(shù)的意義鄙漏。第二部分是值的來源和決定因素,onMeasure()方法中值由父類傳遞過來棺蛛,其值是根據(jù)父類的mode和自身的LayoutParams決定泥张。如果還感覺很模糊,不妨自己去寫個例子去驗(yàn)證一下鞠值,這樣可以加深印象,最重要的還是要理解渗钉。