重溫Android開發藝術探索之四 View工做原理

View 的工做原理

ViewRoot 和DecorView

ViewRoot對應ViewRootImpl,是鏈接WindowManager 和DecorView的紐帶。View的三大流程都是經過ViewRoot完成的,如圖所示, ![ViewRootImpl 的代碼結構圖] java

而且當系統一些常量發生改變時也是有它監聽到了處理的 在ActivityThread的attach方法中有添加這個監聽,源碼以下

ViewRootImpl.addConfigurationChange(Configuration newConfig),經過mH 發送消息 what = 118 的值到H 中處理 調用了 ActivityThread 的 handleConfigurationChanged
複製代碼

能夠說ViewRootImpl控制這整個View的繪製流程,是很是重要的,當Activity被建立時,會將DecorView添加到Window中,同時會建立ViewRootImpl對象.api

ActivityThread#handleResumeActivity wm.addView(decor,l),經過WindowManager 將decor添加到window中,l是LayoutParams,核心代碼以下:api25的源碼ide

WindowManager wm;
                if (r.window == null && !a.mFinished && willBeVisible) {
                    r.window = r.activity.getWindow();
                    View decor = r.window.getDecorView();
                    decor.setVisibility(4);
                    wm = a.getWindowManager();
                    LayoutParams l = r.window.getAttributes();
                    a.mDecor = decor;
                    l.type = 1;
                    l.softInputMode |= forwardBit;
                    if (r.mPreserveWindow) {
                        a.mWindowAdded = true;
                        r.mPreserveWindow = false;
                        ViewRootImpl impl = decor.getViewRootImpl();
                        if (impl != null) {
                            impl.notifyChildRebuilt();
                        }
                    }

                    if (a.mVisibleFromClient && !a.mWindowAdded) {
                        a.mWindowAdded = true;
                        wm.addView(decor, l);
                    }
                } else if (!willBeVisible) {
                    r.hideForNow = true;
                }
複製代碼

wm.addView(decor, l)通過 WindowManagerImpl再到WindowManagerGlobal 中的 addView方法,在此方法裏面對ViewRootImpl 進行了初始化。post

ViewRootImpl root = new ViewRootImpl(view.getContext(), display);
 root.setView(view, wparams, panelParentView);
複製代碼

View的繪製流程是從performTraversals執行遍歷開始的,通過measure,layout,draw,三個過程才能最終將一個View繪製出來。測試

MeasureSpec 測量規格

MeasureSpec

一個32爲的int值,高2位表明SpecMode 低30位表明SpecSize。ui

SpecMode

  • UNSPECIFIED 父容器不對View作任何限制,要多大給多大。
  • EXACTLY 父容器已經檢測出了View所須要的精確大小,這時候View的最終大小就是SpecSize所指定的值,對應與LayoutParams中的match_parent和具體數值兩種模式。
  • AT_MOST 父容器指定了一個最大值,View不能超過這個最大值,對應wrap_content。

MeasureSpec 和 layoutParams 的對應關係

  • DecorView 其MeasureSpec是由自身的LayoutParams和窗口的尺寸來共同決定spa

  • 普通的View 其MeasureSpec是由父容器的MeasureSpec和自身的LayoutParams來共同決定code

  • 簡單來講自定義Veiw到measure這一步的時候,須要測試自身的寬高,須要widthMeasureSpec, heightMeasureSpec,寬的測量規格和高的測量規格,二者的獲取方式都是同樣,須要根據父容器的MeasureSpec 和自身的 寬高來獲得。orm

  • View的measure過程,到這一步確定是通過了父控件的measure過程,獲得了父控件傳下來的measureSpec,而後調用onMeasure,經過setMeasuredDimension 來肯定自身的寬高。固然中間須要通過一些計算獲得widthMeasureSpec, heightMeasureSpec。cdn

Activity啓動時去獲取某一View的寬高

  • Activity/View#onWindowFocusChanged。

  • ViewTreeObserver.addOnGlobalLayoutListener()。

總結

從ViewRootImpl的構造方法中調用 loadSystemProperties()

->profileRendering()

->scheduleTraversals()

-> mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);

->doTraversal();

->performTraversals();

->performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);

-> performLayout(lp, mWidth, mHeight);

-> performDraw();

完成View的三大流程。

相關文章
相關標籤/搜索