android的view繪製流程

首先明確幾個概念

Activity基本的頁面單元Activity包含一個Window,window上可以繪製各種view

View:最基本的UI組件,表示屏幕上的一個矩形區域;

Window:表示頂層窗口,管理界面的顯示和事件的響應;每個Activity 均會創建一個PhoneWindow對象,是Activity和整個View系統交互的接口。

PhoneWindow類:該類繼承於Window類,同時,PhoneWindow類內部包含了一個DecorView對象。簡而言之,PhoneWindow是把一個FrameLayout進行了一定的包裝,並提供了一組通用的窗口操作接口。

DecorView:是Window中View的RootView,設置窗口屬性;該類是一個FrameLayout的子類,並且是PhoneWindow中的一個內部類。Decor的英文是Decoration,即“修飾”的意思,DecorView就是對普通的FrameLayout進行了一定的修飾,比如添加一個通用的Title bar,並響應特定的按鍵消息等。

ViewRoot:它並不是一個View類型,而是一個Handler。

它的主要作用如下:

A. 向DecorView分發收到的用戶發起的event事件,如按鍵,觸屏,軌跡球等事件;

B. 與WindowManagerService交互,完成整個Activity的GUI的繪製




3.

  整個View樹的繪圖流程是在ViewRoot.java類的performTraversals()函數展開的,該函數做的執行過程可簡單概況爲

 根之前狀態,判斷是否需要重新計算視圖大小(measure)、是否重新需要安置視圖的位置(layout)、以及是否需要重繪

 (draw),其框架過程如下:

                                                                                                   步驟其實爲host.layout() 

           

 

 

      接下來溫習一下整個View樹的結構,對每個具體View對象的操作,其實就是個遞歸的實現。

 

                   

4.mesarue():

爲整個View樹計算實際的大小,即設置實際的高(對應屬性:mMeasuredHeight)和寬(對應屬性:

  mMeasureWidth),每個View的控件的實際寬高都是由父視圖和本身視圖決定的。

layout:

根據子視圖的大小以及佈局參數將View樹放到合適的位置上

draw():

ViewRoot對象的performTraversals()方法調用draw()方法發起繪製該View樹,值得注意的是每次發起繪圖時,並不

  會重新繪製每個View樹的視圖,而只會重新繪製那些“需要重繪”的視圖,View類內部變量包含了一個標誌位DRAWN,當該

視圖需要重繪時,就會爲該View添加該標誌位。


5.invalidate()方法 :

請求重繪View樹,即draw()過程,假如視圖發生大小沒有變化就不會調用layout()過程,並且只繪製那些“需要重繪的”

視圖,即誰(View的話,只繪製該View ;ViewGroup,則繪製整個ViewGroup)請求invalidate()方法,就繪製該視圖。


6.

requestLayout()方法 :會導致調用measure()過程 和 layout()過程 。

說明:只是對View樹重新佈局layout過程包括measure()和layout()過程,不會調用draw()過程,但不會重新繪製

任何視圖包括該調用者本身。


7.measure過程:

View樹是遍歷繪製的,內部的主體邏輯是判斷是否需要重新測量視圖大小(measure),是否需要重新佈局(layout),是否重新需要繪製(draw)。measure過程是遍歷的前提,只有measure後才能進行佈局(layout)和繪製(draw),因爲在layout的過程中需要用到measure過程中計算得到的每個View的測量大小,而draw過程需要layout確定每個view的位置才能進行繪製。下面我們主要來探討一下measure的主要過程,相對與layout和draw,measure過程理解起來比較困難。

      我們在編寫layout的xml文件時會碰到layout_width和layout_height兩個屬性,對於這兩個屬性我們有三種選擇:賦值成具體的數值,match_parent或者wrap_content,而measure過程就是用來處理match_parent(低版本叫fill_parent)或者wrap_content,假如layout中規定所有View的layout_width和layout_height必須賦值成具體的數值,那麼measure其實是沒有必要的,但是google在設計Android的時候考慮加入match_parent或者wrap_content肯定是有原因的,它們會使得佈局更加靈活。

看下measue(int widthMeasureSpec, int heightMeasureSpec)中的兩個參數, 這兩個參數分別是父視圖提供的測量規格,當父視圖調用子視圖的measure函數對子視圖進行測量時,會傳入這兩個參數,通過這兩個參數以及子視圖本身的LayoutParams來共同決定子視圖的測量規格,在ViewGroup的measureChildWithMargins函數中體現了這個過程,稍後會介紹。

     MeasureSpec參數的值爲int型,分爲高32位和低16爲,高32位保存的是specMode,低16位表示specSize,specMode分三種:

      1、MeasureSpec.UNSPECIFIED(int值爲0),父視圖不對子視圖施加任何限制,子視圖可以得到任意想要的大小;

      2、MeasureSpec.EXACTLY,父視圖希望子視圖的大小是specSize中指定的大小;

      3、MeasureSpec.AT_MOST,子視圖的大小最多是specSize中的大小。

父視圖對子視圖無限制時,一般使用measue(0,0),即specModeMeasureSpec.UNSPECIFIED

MeasureSpec有3種模式分別是UNSPECIFIED, EXACTLY和AT_MOST, 那麼這些模式和我們平時設置的layout參數fill_parent, wrap_content有什麼關係呢。經過代碼測試就知道,當我們設置width或height爲fill_parent時,容器在佈局時調用子view的measure方法傳入的模式是EXACTLY,因爲子view會佔據剩餘容器的空間,所以它大小是確定的。而當設置爲wrap_content時,容器傳進去的是AT_MOST, 表示子view的大小最多是多少,這樣子view會根據這個上限來設置自己的尺寸。當子view的大小設置爲精確值時,容器傳入的是EXACTLY  

8.fill_parent應該是子view會佔據剩下容器的空間,而不會覆蓋前面已佈局好的其他view空間,當然後面佈局子view就沒有空間給分配了,所以fill_parent屬性對佈局順序很重要。以前所想的是把所有容器的空間都佔滿了,難怪google在2.2版本里把fill_parent的名字改爲match_parent.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章