Android中View繪製流程

來源:互聯網
上載者:User

1.推薦閱讀:《Android核心剖析》

2.UI架構基本概念:

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.

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.