天天看點

View的測量、布局和繪制過程中的關鍵方法

文章目錄

      • 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的。

如下圖所示:

View的測量、布局和繪制過程中的關鍵方法

本文的源碼基于

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);

}