天天看點

Android View的繪制流程簡單總結

原文連結:https://www.jianshu.com/p/5a71014e7b1b

一、啟動:每個Activity 均會建立一個 PhoneWindow對象,是Activity和整個View系統互動的接口,每個Window都對應着一個View和一個ViewRootImpl,Window和View通過ViewRootImpl來建立聯系,對于Activity來說,ViewRootImpl是連接配接WindowManager和DecorView的紐帶,繪制的入口是由ViewRootImpl的performTraversals方法來發起Measure,Layout,Draw等流程的。

二、Measure過程:

1、MeasureSpec 的了解

是view的一個大小和模式的組合,

  • MeasureSpec中的值是一個整型(32位)将size和mode打包成一個Int型,其中高兩位是mode,後面30位存的是size,是為了減少對象的配置設定開支。MeasureSpec 類似于下圖,隻不過這邊用的是十進制的數,而MeasureSpec 是二進制存儲的。
  • Android View的繪制流程簡單總結
  • MesureSpec三種模式

UPSPECIFIED : 父容器對于子容器沒有任何限制,子容器想要多大就多大

EXACTLY: 父容器已經為子容器設定了尺寸,子容器應當服從這些邊界,不論子容器想要多大的空間。

AT_MOST:子容器可以是聲明大小内的任意大小

父View的measure的過程會先測量子View,等子View測量結果出來後,再來測量自己,上面的measureChildWithMargins就是用來測量某個子View的,通過父view的measurespec的model确定子view的resultmodel和resultsize;

  • 如果父View的MeasureSpec 是EXACTLY,說明父View的大小是确切的,(确切的意思很好了解,如果一個View的MeasureSpec 是EXACTLY,那麼它的size 是多大,最後展示到螢幕就一定是那麼大)。
  • 如果父View的MeasureSpec 是AT_MOST,說明父View的大小是不确定,最大的大小是MeasureSpec 的size值,不能超過這個值。
  • 如果父View的MeasureSpec 是UNSPECIFIED(未指定),表示沒有任何束縛和限制,不像AT_MOST表示最大隻能多大,不也像EXACTLY表示父View确定的大小,子View可以得到任意想要的大小,不受限制

ViewGroup的Measure過程

ViewGroup并沒有實作onMeasure,他會根據具體實作的onMeasure來measure所有子view的寬高。

三、layout

1.performTraversals 方法執行完mView.measure 計算出mMeasuredXXX後就開始執行layout 函數來确定View具體放在哪個位置,我們計算出來的View目前隻知道view矩陣的大小,具體這個矩陣放在哪裡,這就是layout 的工作了。 2.如果目前ViewGroup未添加LayoutTransition動畫,或者LayoutTransition動畫此刻并未運作,那麼調用super.layout(l, t, r, b),繼而調用到ViewGroup中的onLayout,否則将mLayoutSuppressed設定為true,等待動畫完成時再調用requestLayout()。 3.然後調用setFrame(l, t, r, b) 可以了解為給mLeft 、mTop、mRight、mBottom指派 4.回調onLayout,對于View來說,onLayout隻是一個空實作,對于viewgroup來說,onLayout是一個抽象方法,繼承viewgroup需實作onLayout,一般是周遊子view然後調用子view的layout方法

四、draw

performTraversals 方法的下一步就是mView.draw(canvas); 因為View的draw 方法一般不去重寫,官網文檔也建議不要去重寫draw 方法

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
    ...
        background.draw(canvas);
    ...
        // skip step 2 & 5 if possible (common case)
    ...
        // Step 2, save the canvas' layers
    ...
        if (solidColor == ) {
            final int flags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;

            if (drawTop) {
                canvas.saveLayer(left, top, right, top + length, null, flags);
            }
    ...
        // 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

        if (drawTop) {
            matrix.setScale(, fadeHeight * topFadeStrength);
            matrix.postTranslate(left, top);
            fade.setLocalMatrix(matrix);
            canvas.drawRect(left, top, right, top + length, p);
        }
    ...
        // Step 6, draw decorations (scrollbars)
        onDrawScrollBars(canvas);
    }
                

1、第一步:背景繪制

2、第三步,對View的内容進行繪制。

3、第4步 對目前View的所有子View進行繪制(dispatchDraw)

4、第6步 對View的滾動條進行繪制

Android View的繪制流程簡單總結

繼續閱讀