MeasureSpec理解

簡要

??今天來聊聊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);
}

舉例:
MeasureSpec運(yùn)算.png

位運(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代替
MeasureSpec樹狀圖.png

上圖說明:
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)證一下鞠值,這樣可以加深印象,最重要的還是要理解渗钉。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末彤恶,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子鳄橘,更是在濱河造成了極大的恐慌声离,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瘫怜,死亡現(xiàn)場離奇詭異术徊,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)鲸湃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進(jìn)店門赠涮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來子寓,“玉大人,你說我怎么就攤上這事笋除⌒庇眩” “怎么了?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵垃它,是天一觀的道長鲜屏。 經(jīng)常有香客問我,道長国拇,這世上最難降的妖魔是什么洛史? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮酱吝,結(jié)果婚禮上也殖,老公的妹妹穿的比我還像新娘。我一直安慰自己掉瞳,他們只是感情好毕源,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著陕习,像睡著了一般霎褐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上该镣,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天冻璃,我揣著相機(jī)與錄音,去河邊找鬼损合。 笑死省艳,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的嫁审。 我是一名探鬼主播跋炕,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼律适!你這毒婦竟也來了辐烂?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤捂贿,失蹤者是張志新(化名)和其女友劉穎纠修,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體厂僧,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡扣草,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片辰妙。...
    茶點(diǎn)故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡鹰祸,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出上岗,到底是詐尸還是另有隱情福荸,我是刑警寧澤,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布肴掷,位于F島的核電站敬锐,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏呆瞻。R本人自食惡果不足惜台夺,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望痴脾。 院中可真熱鬧颤介,春花似錦、人聲如沸赞赖。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽前域。三九已至辕近,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間匿垄,已是汗流浹背移宅。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留椿疗,地道東北人漏峰。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像届榄,于是被迫代替她去往敵國和親浅乔。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評論 2 353

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