Android View框架總結(六)View佈局流程之Draw過程
阿新 • • 發佈:2019-01-30
- 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()方法提供不同順序。