天天看點

Android View 繪制流程(Draw) 完全解析前言從performDraw說起View的繪制

前幾篇文章,筆者分别講述了DecorView,measure,layout流程等,接下來将詳細分析三大工作流程的最後一個流程——繪制流程。測量流程決定了View的大小,布局流程決定了View的位置,那麼繪制流程将決定View的樣子,一個View該顯示什麼由繪制流程完成。以下源碼均取自Android API 21。

前面幾篇文章提到,三大工作流程始于ViewRootImpl#performTraversals,在這個方法内部會分别調用performMeasure,performLayout,performDraw三個方法來分别完成測量,布局,繪制流程。那麼我們現在先從performDraw方法看起,ViewRootImpl#performDraw:

裡面又調用了ViewRootImpl#draw方法,并傳遞了fullRedrawNeeded參數,而該參數由mFullRedrawNeeded成員變量擷取,它的作用是判斷是否需要重新繪制全部視圖,如果是第一次繪制視圖,那麼顯然應該繪制是以的視圖,如果由于某些原因,導緻了視圖重繪,那麼就沒有必要繪制所有視圖。我們來看看ViewRootImpl#draw:

這裡省略了一部分代碼,我們隻看關鍵代碼,首先是先擷取了mDirty值,該值儲存了需要重繪的區域的資訊,關于視圖重繪,後面會有文章專門叙述,這裡先熟悉一下。接着根據fullRedrawNeeded來判斷是否需要重置dirty區域,最後調用了ViewRootImpl#drawSoftware方法,并把相關參數傳遞進去,包括dirty區域,我們接着看該方法的源碼:

可以看書,首先是執行個體化了Canvas對象,然後鎖定該canvas的區域,由dirty區域決定,接着對canvas進行一系列的屬性指派,最後調用了mView.draw(canvas)方法,前面分析過,mView就是DecorView,也就是說從DecorView開始繪制,前面所做的一切工作都是準備工作,而現在則是正式開始繪制流程。

由于ViewGroup沒有重寫draw方法,是以所有的View都是調用View#draw方法,是以,我們直接看它的源碼:

可以看到,draw過程比較複雜,但是邏輯十厘清晰,而官方注釋也清楚地說明了每一步的做法。我們首先來看一開始的标記位dirtyOpaque,該标記位的作用是判斷目前View是否是透明的,如果View是透明的,那麼根據下面的邏輯可以看出,将不會執行一些步驟,比如繪制背景、繪制内容等。這樣很容易了解,因為一個View既然是透明的,那就沒必要繪制它了。接着是繪制流程的六個步驟,這裡先小結這六個步驟分别是什麼,然後再展開來講。

繪制流程的六個步驟:

1、對View的背景進行繪制

2、儲存目前的圖層資訊(可跳過)

3、繪制View的内容

4、對View的子View進行繪制(如果有子View)

5、繪制View的褪色的邊緣,類似于陰影效果(可跳過)

6、繪制View的裝飾(例如:滾動條)

其中第2步和第5步是可以跳過的,我們這裡不做分析,我們重點來分析其它步驟。

這裡調用了View#drawBackground方法,我們看它的源碼:

可以看出,這裡考慮到了view的偏移參數,scrollX和scrollY,繪制背景在偏移後的view中繪制。

這裡調用了View#onDraw方法,View中該方法是一個空實作,因為不同的View有着不同的内容,這需要我們自己去實作,即在自定義View中重寫該方法來實作。

如果目前的View是一個ViewGroup類型,那麼就需要繪制它的子View,這裡調用了dispatchDraw,而View中該方法是空實作,實際是ViewGroup重寫了這個方法,那麼我們來看看,ViewGroup#dispatchDraw:

源碼很長,這裡簡單說明一下,裡面主要周遊了是以子View,每個子View都調用了drawChild這個方法,我們找到這個方法,ViewGroup#drawChild:

可以看出,這裡調用了View的draw方法,但這個方法并不是上面所說的,因為參數不同,我們來看看這個方法,View#draw:

我們主要來看核心部分,首先判斷是否已經有緩存,即之前是否已經繪制過一次了,如果沒有,則會調用draw(canvas)方法,開始正常的繪制,即上面所說的六個步驟,否則利用緩存來顯示。

這一步也可以歸納為ViewGroup繪制過程,它對子View進行了繪制,而子View又會調用自身的draw方法來繪制自身,這樣不斷周遊子View及子View的不斷對自身的繪制,進而使得View樹完成繪制。

所謂的繪制裝飾,就是指View除了背景、内容、子View的其餘部分,例如滾動條等,我們看View#onDrawForeground:

可以看出,邏輯很清晰,和一般的繪制流程非常相似,都是先設定繪制區域,然後利用canvas進行繪制,這裡就不展開詳細地說了,有興趣的可以繼續了解下去。

那麼,到目前為止,View的繪制流程也講述完畢了,希望這篇文章對你們起到幫助作用,謝謝你們的閱讀。

更多閱讀 <a href="http://www.jianshu.com/p/3299c3de0b7d" target="_blank">Android View 測量流程(Measure)完全解析</a> <a href="http://www.jianshu.com/p/836bfdc36407" target="_blank">Android View 布局流程(Layout)完全解析</a>     本文轉自 一點點征服   部落格園部落格,原文連結 http://www.cnblogs.com/ldq2016/p/6689463.html :,如需轉載請自行聯系原作者

繼續閱讀