1. 程式人生 > >Android View框架總結(六)View佈局流程之Draw過程

Android View框架總結(六)View佈局流程之Draw過程

  • View的Draw時序圖
  • ViewRootImpl.performTraversals過程
  • ViewRootImpl.performDraw過程
  • View.draw方法
  • View.dispatchDraw過程
  • LinearLayout的onDraw過程

View的Draw時序圖

這裡寫圖片描述

前面幾篇通過對View樹的measure和layout過程分析事,接下來將結合前兩步得到的測量值及在檢視中的位位置,開始進行繪製操作,一步比一步複雜,draw過程比前面都要複雜,draw的不好,就會出現overdraw。下面請仔細看分析過程: 
draw的原始觸發點還是在ViewRootImpl的performTraversals(執行遍歷)方法中,開始分析流程:可以結合我畫的時序圖一起看,方便理解。

ViewRootImpl.performTraversals()

這裡寫圖片描述

ViewRootImpl.performDraw()

這裡寫圖片描述

ViewRootImpl.draw()

這裡寫圖片描述

measure和layout過程直接呼叫的是ViewRootImpl的performMeasure和performLayout方法,draw呼叫的是ViewRootImpl的performDraw()方法,再由performDraw中的draw(boolean fullRedrawNeeded)方法來呼叫ViewTreeObserver中的dispatchOnDraw()方法,進行通知所有掛在view樹上的view開始draw。

ViewTreeObserver.dispatchOnDraw()

這裡寫圖片描述

View.onDraw()

這裡寫圖片描述

對於View.Java和ViewGroup.java,onDraw()預設都是空實現,因為具體View本身是什麼,這就是做框架,提供空間,你要在裡面自定義什麼view是使用者所決定,但是可以提供預設方法。

View.draw()

這裡寫圖片描述 
這裡寫圖片描述這裡寫圖片描述

View的另一個draw方法

這裡寫圖片描述這裡寫圖片描述

View.drawBackground() — // Step 1, draw the background

這裡寫圖片描述這裡寫圖片描述

View.onDraw() — // Step 3, draw the content

這裡寫圖片描述

View.dispatchDraw() — // Step 4, draw the children

這裡寫圖片描述

View.onDrawScrollBars — // Step 6, draw decorations (foreground, scrollbars)

這裡寫圖片描述這裡寫圖片描述這裡寫圖片描述這裡寫圖片描述這裡寫圖片描述這裡寫圖片描述

View中dispatchDraw()預設為空實現,因為其不包含子view,而ViewGroup重寫了dispatchDraw()來對其子view進行繪製,一般自定義view不應該對dispatchDraw()進行過載,因為它已經體現了View系統繪製的流程。那麼,接下來我們繼續分析下ViewGroup中dispatchDraw()的具體流程:

ViewGroup.dispatchDraw()

這裡寫圖片描述這裡寫圖片描述

ViewGroup.drawChild()

這裡寫圖片描述

  • dispatchDraw()的關鍵就是通過for迴圈呼叫drawChild()對ViewGroup的每個子檢視進行繪製,上述程式碼中如果FLAG_USE_CHILD_DRAWING_ORDER為true,則子檢視的繪製順序通過getChildDrawingOrder來決定,預設的繪製順序即是子檢視加入ViewGroup的順序,而我們可以過載getChildDrawingOrder方法來更改預設的繪製順序,讓子view重疊在父view上,或者說是掛在父view上。
  • drawChild()的核心就是為子檢視分配合適的canvas畫布區,畫布區的size是d在layout過程決定的,而畫布區的位置取決於滾動值以及子檢視當前的動畫。設定畫布區後就會呼叫childview的draw()函式,如果childview的包含SKIP_DRAW標識,僅呼叫dispatchDraw(),即跳過子檢視本身的繪製,但要繪製檢視可能包含的childview。

ViewGroup.drawChild()

這裡寫圖片描述

這裡寫圖片描述

Draw過程小總結:

  • 自定義View是一個ViewGroup,則需要遞迴繪製包含的所有子View。
  • View預設不會繪製任何內容,真正的繪製都需要自己在子類中實現,只是做好繪製流程,這就是框架的職責。
  • 區分View動畫和ViewGroup佈局動畫,前者指的是View自身的動畫,可以通過setAnimation新增,後者是專門針對ViewGroup顯示內部子檢視時設定的動畫,可以在xml佈局檔案中對ViewGroup設定layoutAnimation屬性(譬如對LinearLayout設定子View在顯示時出現逐行、隨機、下等顯示等不同動畫效果)。
  • 在獲取畫布剪下區(每個View的draw中傳入的Canvas)時會自動處理掉padding,子View獲取Canvas不用關注這些邏輯,只用關心如何繪製即可。
  • 預設情況下子View的ViewGroup.drawChild繪製順序和子View被新增的順序一致,但是你也可以過載ViewGroup.getChildDrawingOrder()方法提供不同順序。