文章目錄
-
-
- View的測量、布局和繪制過程中的關鍵方法
-
- 流程概覽
- Measure流程
- Layout流程
- Draw流程
-
View的測量、布局和繪制過程中的關鍵方法
我們這裡說的View的測量、布局和繪制,實質上是針對ViewGroup的,簡單起見就不區分View和ViewGroup。View的測量、布局和繪制是包含在ViewGroup流程中的。
流程概覽
View的測量、布局、繪制是從
ViewRootImpl
開始的,它是Activity中的根View。具體是在
ViewRootImpl#performTraversals
方法中依次調用performMeasure、performLayout、performDraw。
我們常見的更新UI的方法
View#invalidate
最終就是通過調用ViewRootImpl#performTraversals方法來完成的,具體參見View#invalidate方法是如何更新UI的。
如下圖所示:
本文的源碼基于
android-28
的api。
Measure流程
Measure的具體流程如下:
ViewRootImpl#performTreversals() -->
ViewRootImpl#performMeasure() -->
View#measure() -->
View#onMeasure
如果是ViewGroup,一般會重寫View#onMeasure方法,會在ViewGroup#onMeasure方法中循環調用子View的measure方法,如此循環往複,直到最終調用了所有的View#onMeasure方法。
Layout流程
Layout的具體流程如下:
ViewRootImpl#performTreversals() -->
ViewRootImpl#performLayout() -->
View#layout() -->
View#onLayout()
View#onLayout方法預設空實作,如下所示,
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
}
是以onLayout方法需要View根據需求各自實作。
與onMeasure方法類似,ViewGroup一般會重寫View#onLayout方法。
然後在ViewGroup#onLayout方法中循環調用子View的layout方法,如此循環往複,直到最終調用了所有的View#onLayout方法。
這裡以LinearLayout為例,
LinearLayout#onLayout -->
Linearlayout#layoutVertical -->
循環周遊每個子View,調用Linearlayout#setChildFrame -->
最終調用View#layout方法。
Draw流程
Draw的具體流程如下:
ViewRootImpl#performTreversals() -->
ViewRootImpl#performDraw() -->
ViewRootImpl#draw(boolean fullRedrawNeeded) -->
View#drawSoftware -->
View#draw(Canvas canvas) -->
繪制目前View:View#onDraw(Canvas canvas) -->
繪制子View:View#dispatchDraw(Canvas canvas)
在
View#draw(Canvas canvas)
方法中,有如下注釋:
/*
* Draw traversal performs several drawing steps which must be executed
* in the appropriate order:
*
* 1. Draw the background
* 2. If necessary, save the canvas' layers to prepare for fading
* 3. Draw view's content
* 4. Draw children
* 5. If necessary, draw the fading edges and restore layers
* 6. Draw decorations (scrollbars for instance)
*/
繪制流程按照以下六個步驟執行:
①繪制背景
②如果需要,儲存圖層
③繪制目前View的内容
④繪制子View
⑤如果需要,繪制邊界,恢複圖層
⑥繪制相關裝飾(比如滾動條)
對應的源碼為:為了友善閱讀,省略了無關代碼。
public void draw(Canvas canvas) {
...
/*
* Draw traversal performs several drawing steps which must be executed
* in the appropriate order:
*
* 1. Draw the background
* 2. If necessary, save the canvas' layers to prepare for fading
* 3. Draw view's content
* 4. Draw children
* 5. If necessary, draw the fading edges and restore layers
* 6. Draw decorations (scrollbars for instance)
*/
// Step 1, draw the background, if needed
if (!dirtyOpaque) {
drawBackground(canvas);
}
...
// Step 2, save the canvas' layers
...
saveCount = canvas.getSaveCount();
// Step 3, draw the content
if (!dirtyOpaque) onDraw(canvas);
// Step 4, draw the children
dispatchDraw(canvas);
// Step 5, draw the fade effect and restore layers
...
canvas.restoreToCount(saveCount);
drawAutofilledHighlight(canvas);
// Step 6, draw decorations (foreground, scrollbars)
onDrawForeground(canvas);
}