Android中mesure过程详解
2019獨(dú)角獸企業(yè)重金招聘Python工程師標(biāo)準(zhǔn)>>>
invalidate()最后會(huì)發(fā)起一個(gè)View樹遍歷的請(qǐng)求,并通過執(zhí)行performTraersal()來響應(yīng)該請(qǐng) 求,performTraersal()正是對(duì)View樹進(jìn)行遍歷和繪制的核心函數(shù),內(nèi)部的主體邏輯是判斷是否需要重新測(cè)量視圖大小(measure), 是否需要重新布局(layout),是否重新需要繪制(draw)。measure過程是遍歷的前提,只有measure后才能進(jìn)行布局(layout) 和繪制(draw),因?yàn)樵趌ayout的過程中需要用到measure過程中計(jì)算得到的每個(gè)View的測(cè)量大小,而draw過程需要layout確定每 個(gè)view的位置才能進(jìn)行繪制。下面我們主要來探討一下measure的主要過程,相對(duì)與layout和draw,measure過程理解起來比較困難。
? ? ? 我們?cè)诰帉憀ayout的xml文件時(shí)會(huì)碰到layout_width和layout_height兩個(gè)屬性,對(duì)于這兩個(gè)屬性我們有三種選擇:賦值成具體 的數(shù)值,match_parent或者wrap_content,而measure過程就是用來處理match_parent或者 wrap_content,假如layout中規(guī)定所有View的layout_width和layout_height必須賦值成具體的數(shù)值,那么 measure其實(shí)是沒有必要的,但是google在設(shè)計(jì)Android的時(shí)候考慮加入match_parent或者wrap_content肯定是有原 因的,它們會(huì)使得布局更加靈活。
? ? ? 首先我們來看幾個(gè)關(guān)鍵的函數(shù)和參數(shù):
? ? ? 1、public final void measue(int widthMeasureSpec, int heightMeasureSpec);
? ? ? 2、protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec);
? ? ? 3、protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec)
? ? ? 4、protected void measureChild(View child, int parentWidthMeasureSpec, int parentHeightMeasureSpec)
? ? ? 5、protected void measureChildWithMargins(View child,int parentWidthMeasureSpec, int widthUsed,?int parentHeightMeasureSpec, int heightUsed)
? ? ?接著我們來看View類中measure和onMeasure函數(shù)的源碼:
public?final?void?measure(int?widthMeasureSpec,?int?heightMeasureSpec)?{????????if?((mPrivateFlags?&?FORCE_LAYOUT)?==?FORCE_LAYOUT?||widthMeasureSpec?!=?mOldWidthMeasureSpec?||heightMeasureSpec?!=?mOldHeightMeasureSpec)?{????????????//?first?clears?the?measured?dimension?flagmPrivateFlags?&=?~MEASURED_DIMENSION_SET;????????????if?(ViewDebug.TRACE_HIERARCHY)?{ViewDebug.trace(this,?ViewDebug.HierarchyTraceType.ON_MEASURE);}????????????//?measure?ourselves,?this?should?set?the?measured?dimension?flag?back????????????onMeasure(widthMeasureSpec,?heightMeasureSpec);????????????//?flag?not?set,?setMeasuredDimension()?was?not?invoked,?we?raise????????????//?an?exception?to?warn?the?developerif?((mPrivateFlags?&?MEASURED_DIMENSION_SET)?!=?MEASURED_DIMENSION_SET)?{????????????????throw?new?IllegalStateException("onMeasure()?did?not?set?the"+?"?measured?dimension?by?calling"+?"?setMeasuredDimension()");}mPrivateFlags?|=?LAYOUT_REQUIRED;}mOldWidthMeasureSpec?=?widthMeasureSpec;mOldHeightMeasureSpec?=?heightMeasureSpec;}
?
由于函數(shù)原型中有final字段,那么measure根本沒打算被子類繼承,也就是說measure的過程是固定的,而measure中調(diào)用了onMeasure函數(shù),因此真正有變數(shù)的是onMeasure函數(shù),onMeasure的默認(rèn)實(shí)現(xiàn)很簡(jiǎn)單,源碼如下:
protected?void?onMeasure(int?widthMeasureSpec,?int?heightMeasureSpec)?{setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(),?widthMeasureSpec),getDefaultSize(getSuggestedMinimumHeight(),?heightMeasureSpec));}onMeasure默認(rèn)的實(shí)現(xiàn)僅僅調(diào)用了setMeasuredDimension,setMeasuredDimension函數(shù)是一個(gè)很關(guān)鍵的函數(shù),它對(duì)View的成員變量mMeasuredWidth和mMeasuredHeight變量賦值,而measure的主要目的就是對(duì)View樹中的每個(gè)View的mMeasuredWidth和mMeasuredHeight進(jìn)行賦值,一旦這兩個(gè)變量被賦值,則意味著該View的測(cè)量工作結(jié)束。
?
protected?final?void?setMeasuredDimension(int?measuredWidth,?int?measuredHeight)?{mMeasuredWidth?=?measuredWidth;mMeasuredHeight?=?measuredHeight;mPrivateFlags?|=?MEASURED_DIMENSION_SET;}?
對(duì)于非ViewGroup的View而言,通過調(diào)用上面默認(rèn)的measure——>onMeasure,即可完成View的測(cè)量,當(dāng)然你也可 以重載onMeasure,并調(diào)用setMeasuredDimension來設(shè)置任意大小的布局,但一般不這么做,因?yàn)檫@種做法太“專政”,至于為何“專政”,讀完本文就會(huì)明白。
? ? ? 對(duì)于ViewGroup的子類而言,往往會(huì)重載onMeasure函數(shù)負(fù)責(zé)其children的measure工作,重載時(shí)不要忘記調(diào)用 setMeasuredDimension來設(shè)置自身的mMeasuredWidth和mMeasuredHeight。如果我們?cè)趌ayout的時(shí)候不 需要依賴子視圖的大小,那么不重載onMeasure也可以,但是必須重載onLayout來安排子視圖的位置,這在下一篇博客中會(huì)介紹。 ?
? ? ? 再來看下measue(int widthMeasureSpec, int heightMeasureSpec)中的兩個(gè)參數(shù), 這兩個(gè)參數(shù)分別是父視圖提供的測(cè)量規(guī)格,當(dāng)父視圖調(diào)用子視圖的measure函數(shù)對(duì)子視圖進(jìn)行測(cè)量時(shí),會(huì)傳入這兩個(gè)參數(shù),通過這兩個(gè)參數(shù)以及子視圖本身的 LayoutParams來共同決定子視圖的測(cè)量規(guī)格,在ViewGroup的measureChildWithMargins函數(shù)中體現(xiàn)了這個(gè)過程,稍后會(huì)介紹。
? ? ?MeasureSpec參數(shù)的值為int型,分為高32位和低16為,高32位保存的是specMode,低16位表示specSize,specMode分三種:
? ? ? 1、MeasureSpec.UNSPECIFIED,父視圖不對(duì)子視圖施加任何限制,子視圖可以得到任意想要的大小;
? ? ? 2、MeasureSpec.EXACTLY,父視圖希望子視圖的大小是specSize中指定的大小;
? ? ? 3、MeasureSpec.AT_MOST,子視圖的大小最多是specSize中的大小。
? ? ? 以上施加的限制只是父視圖“希望”子視圖的大小按MeasureSpec中描述的那樣,但是子視圖的具體大小取決于多方面的。
? ? ? ViewGroup中定義了measureChildren, measureChild, ?measureChildWithMargins來對(duì)子視圖進(jìn)行測(cè)量,measureChildren內(nèi)部只是循環(huán)調(diào)用 measureChild,measureChild和measureChildWithMargins的區(qū)別就是是否把margin和padding也 作為子視圖的大小,我們主要分析measureChildWithMargins的執(zhí)行過程:
?
????protected?void?measureChildWithMargins(View?child,????????????int?parentWidthMeasureSpec,?int?widthUsed,????????????int?parentHeightMeasureSpec,?int?heightUsed)?{????????final?MarginLayoutParams?lp?=?(MarginLayoutParams)?child.getLayoutParams();????????final?int?childWidthMeasureSpec?=?getChildMeasureSpec(parentWidthMeasureSpec,mPaddingLeft?+?mPaddingRight?+?lp.leftMargin?+?lp.rightMargin????????????????????????+?widthUsed,?lp.width);????????final?int?childHeightMeasureSpec?=?getChildMeasureSpec(parentHeightMeasureSpec,mPaddingTop?+?mPaddingBottom?+?lp.topMargin?+?lp.bottomMargin????????????????????????+?heightUsed,?lp.height);child.measure(childWidthMeasureSpec,?childHeightMeasureSpec);}?
總的來看該函數(shù)就是對(duì)父視圖提供的measureSpec參數(shù)進(jìn)行了調(diào)整(結(jié)合自身的LayoutParams參數(shù)),然后再來調(diào)用child.measure()函數(shù),具體通過函數(shù)getChildMeasureSpec來進(jìn)行參數(shù)調(diào)整,過程如下:
public?static?int?getChildMeasureSpec(int?spec,?int?padding,?int?childDimension)?{????????int?specMode?=?MeasureSpec.getMode(spec);????????int?specSize?=?MeasureSpec.getSize(spec);????????int?size?=?Math.max(0,?specSize?-?padding);????????int?resultSize?=?0;????????int?resultMode?=?0;????????switch?(specMode)?{????????//?Parent?has?imposed?an?exact?size?on?uscase?MeasureSpec.EXACTLY:????????????if?(childDimension?>=?0)?{resultSize?=?childDimension;resultMode?=?MeasureSpec.EXACTLY;}?else?if?(childDimension?==?LayoutParams.MATCH_PARENT)?{????????????????//?Child?wants?to?be?our?size.?So?be?it.resultSize?=?size;resultMode?=?MeasureSpec.EXACTLY;}?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?uscase?MeasureSpec.AT_MOST:????????????if?(childDimension?>=?0)?{????????????????//?Child?wants?a?specific?size...?so?be?itresultSize?=?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?becase?MeasureSpec.UNSPECIFIED:????????????if?(childDimension?>=?0)?{????????????????//?Child?wants?a?specific?size...?let?him?have?itresultSize?=?childDimension;resultMode?=?MeasureSpec.EXACTLY;}?else?if?(childDimension?==?LayoutParams.MATCH_PARENT)?{????????????????//?Child?wants?to?be?our?size...?find?out?how?big?it?should????????????????//?beresultSize?=?0;resultMode?=?MeasureSpec.UNSPECIFIED;}?else?if?(childDimension?==?LayoutParams.WRAP_CONTENT)?{????????????????//?Child?wants?to?determine?its?own?size....?find?out?how????????????????//?big?it?should?beresultSize?=?0;resultMode?=?MeasureSpec.UNSPECIFIED;}????????????break;}????????return?MeasureSpec.makeMeasureSpec(resultSize,?resultMode);}getChildMeasureSpec的總體思路就是通過其父視圖提供的MeasureSpec參數(shù)得到specMode和specSize,并 根據(jù)計(jì)算出來的specMode以及子視圖的childDimension(layout_width和layout_height中定義的)來計(jì)算自身 的measureSpec,如果其本身包含子視圖,則計(jì)算出來的measureSpec將作為調(diào)用其子視圖measure函數(shù)的參數(shù),同時(shí)也作為自身調(diào)用 setMeasuredDimension的參數(shù),如果其不包含子視圖則默認(rèn)情況下最終會(huì)調(diào)用onMeasure的默認(rèn)實(shí)現(xiàn),并最終調(diào)用到 setMeasuredDimension,而該函數(shù)的參數(shù)正是這里計(jì)算出來的。
?
? ? ? 總結(jié):從上面的描述看出,決定權(quán)最大的就是View的設(shè)計(jì)者,因?yàn)樵O(shè)計(jì)者可以通過調(diào)用setMeasuredDimension決定視圖的最終大小,例如 調(diào)用setMeasuredDimension(100, 100)將視圖的mMeasuredWidth和mMeasuredHeight設(shè)置為100,100,那么父視圖提供的大小以及程序員在xml中設(shè)置的 layout_width和layout_height將完全不起作用,當(dāng)然良好的設(shè)計(jì)一般會(huì)根據(jù)子視圖的measureSpec來設(shè)置 mMeasuredWidth和mMeasuredHeight的大小,已尊重程序員的意圖。
轉(zhuǎn)載于:https://my.oschina.net/u/2370693/blog/543119
總結(jié)
以上是生活随笔為你收集整理的Android中mesure过程详解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ***CSS魔法堂:选择器及其优先级
- 下一篇: js文件引用方式及其同步执行与异步执行