4.1 初始ViewRoot和DecorView
View的繪制流程是從ViewRoot的performTraversal的開(kāi)始的跟畅。經(jīng)過(guò)measure,layout,draw三個(gè)過(guò)程汇荐。
performTraversal會(huì)依次調(diào)用performMeasure,performLayout,performDraw來(lái)完成頂級(jí)View的measure,layout和draw過(guò)程。performMeasure方法中會(huì)調(diào)用measure方法龟虎,在measure方法中又會(huì)調(diào)用onMeasure方法锋勺,在onMeasure方法中會(huì)對(duì)所有的子元素進(jìn)行measure過(guò)程,這個(gè)時(shí)候measure流程就從父容器傳遞到子元素了芽狗,這樣就完成了一次measure過(guò)程绢掰,layout和draw的過(guò)程類(lèi)似。
(1)measure過(guò)程決定了View的寬高童擎,幾乎在所有情況下滴劲,這個(gè)寬高都等于View最終的寬高。getMeasuredHeight(),getMeasuredWidth()來(lái)得到測(cè)量寬高顾复。
(2)layout過(guò)程決定了View四個(gè)頂點(diǎn)的位置班挖,和View實(shí)際的寬高。通過(guò)getHeight()和getWidth()來(lái)得到實(shí)際的寬高芯砸。
(3)draw過(guò)程決定view的顯示萧芙。
DecorView是一個(gè)FrameLayout,包含了一個(gè)豎直方向的LinearLayout给梅,上面是標(biāo)題欄,下面是內(nèi)容双揪。
4.1 理解MeasureSpec
(1)MeasureSpec和LayoutParams的對(duì)應(yīng)關(guān)系动羽。
在View測(cè)量的時(shí)候,系統(tǒng)會(huì)將LayoutParams在父容器的約束下轉(zhuǎn)換成MeasureSpec渔期,在根據(jù)MeasureSpec來(lái)決定View測(cè)量后的寬高运吓。
MeasureSpec不是唯一由LayoutParams決定的,而是和父容器一起決定的擎场。對(duì)于DecorView羽德,它的MeasureSpec是由窗口的大小和自身的LayoutParams確定的。
(2)普通view的MeasureSpec的創(chuàng)建規(guī)則
當(dāng)View采用固定寬高時(shí)迅办,不管父容器采用什么MeasureSpec宅静,View的MeasureSpec都是精確模式,大小等于LayoutParams中的大小站欺。
當(dāng)View采用match_parent時(shí)姨夹,如果父容器是精確模式,那么view也是精確模式矾策,大小是父容器剩余的大小磷账。如果父容器是最大模式,那么view也是最大模式贾虽,大小不超過(guò)父容器的剩余空間逃糟。
當(dāng)View采用wrap_content時(shí),不管父容器是什么模式蓬豁,View都是最大模式绰咽,大小是不超過(guò)父容器剩余的大小。
4.3 View的工作流程
(1)measure過(guò)程
view的測(cè)量過(guò)程由measure方法(final)完成地粪,在measure方法中又會(huì)調(diào)用onMeasure方法取募,onMeasure會(huì)調(diào)用setMeasuredDimension方法,setMeasuredDimension會(huì)設(shè)置View寬高的測(cè)量值蟆技。當(dāng)View的SpecMode是AT_MOST和EXACTLY時(shí)玩敏,getDefaultSize返回的是measureSpec中的specSize的大小,而當(dāng)View的SpecMode是UNSPECIFIED的時(shí)候
返回的是size,是getSuggestedMinimumWidth的返回值质礼,如果View沒(méi)有設(shè)置背景旺聚,那么返回的是android:midWidth這個(gè)屬性,如果設(shè)置了背景眶蕉,返回的是android:minWidth和背景最小寬度中兩者的的最大值砰粹。
(2)ViewGroup的measure過(guò)程
viewGroup除了要完成自己的measure以外,還會(huì)遍歷去調(diào)用所有子元素的measure方法妻坝,各個(gè)子元素在遞歸完成此過(guò)程伸眶。ViewGroup是一個(gè)抽象類(lèi),因此它沒(méi)有重寫(xiě)onMeasure方法刽宪。在onMeasure方法中拿到的測(cè)量寬高是不準(zhǔn)確的厘贼,在onLayout中獲得測(cè)量寬高或者最終的寬高。view的measure過(guò)程和Activity的生命周期方法不是同步執(zhí)行的圣拄,因此無(wú)法保證Activity執(zhí)行了onCreate嘴秸、onStart、onResume時(shí)某個(gè)view已經(jīng)測(cè)量完畢了庇谆。如果view還沒(méi)有測(cè)量完畢岳掐,那么獲得的寬高就都是0。下面是四種解決該問(wèn)題的方法:
一:onWindowFocusChanged():View已經(jīng)初始化完畢饭耳,寬高已經(jīng)準(zhǔn)備好了串述。
二:view.post(runnable):通過(guò)post可以將一個(gè)runnable投射到消息隊(duì)列的尾部,然后等待Looper調(diào)用此runnable的時(shí)候寞肖,View就已經(jīng)初始化好了纲酗。
三:ViewTreeObserver:使用ViewTreeObserver的眾多回調(diào)方法可以完成這個(gè)功能,比如使用onGlobalLayoutListener接口新蟆,當(dāng)view樹(shù)的狀態(tài)發(fā)生改變或者view樹(shù)內(nèi)部的view的可見(jiàn)性發(fā)生改變時(shí)觅赊,onGlobalLayout方法將被回調(diào)。伴隨著view樹(shù)的狀態(tài)改變琼稻,這個(gè)方法也會(huì)被多次調(diào)用吮螺。
在view的默認(rèn)實(shí)現(xiàn)中,view的測(cè)量寬高和最終寬高是相等的帕翻,只不過(guò)測(cè)量寬高形成于measure過(guò)程鸠补,而最終寬高形成于layout過(guò)程。
(3)layout過(guò)程
ViewGroup的位置被確定以后熊咽,它在onLayout中會(huì)遍歷所有的子元素并調(diào)用其layout方法莫鸭,在layout方法中OnLayout方法又會(huì)被調(diào)用。layout方法的流程:首先會(huì)通過(guò)setFrame方法來(lái)設(shè)定View的四個(gè)頂點(diǎn)横殴,View一旦確定被因,View在父容器中的位置也被確定了,接著會(huì)調(diào)用onLayout方法衫仑,這個(gè)方法的用途是確定子元素的位置梨与。onLayout的具體實(shí)現(xiàn)和具體的布局有關(guān)。
(4)draw過(guò)程
1.繪制背景:background.draw(canvas)文狱;
2.繪制自己:onDraw()粥鞋;
3.繪制children:dispatchDraw;
4.繪制裝飾:onDrawScrollBars瞄崇。
4.4 自定義view
(1)繼承view重寫(xiě)onDraw方法需要自己支持wrap_content呻粹,并且padding也要自己處理壕曼。繼承特定的View例如TextView不需要考慮。
繼承自ViewGroup要在onMeasure和onLayout中考慮padding和子元素的margin對(duì)其造成的影響等浊。
(2)盡量不要在View中使用Handler腮郊,因?yàn)関iew內(nèi)部本身已經(jīng)提供了post系列的方法,完全可以替代Handler的作用筹燕。
(3)view中如果有線程或者動(dòng)畫(huà)轧飞,需要在onDetachedFromWindow方法中及時(shí)停止。
(4)處理好view的滑動(dòng)沖突情況撒踪。
如何處理wrap_content,原因?
如果在View在布局中使用wrap_content,那么它的specMode是AT_MOST,在這種模式下过咬,它的寬高等于specSize,而specSize等于parentSize制妄。parentSize是父容器目前剩余空間的大小掸绞。和match_parent一致。