當前位置:編程學習大全網 - 源碼下載 - View繪制流程(壹)

View繪制流程(壹)

最近在學習 View 的繪制流程,看了幾篇不錯的博客( ViewRootImpl的獨白,我不是壹個View(布局篇) 、 Android應用層View繪制流程與源碼分析 )自己對照源碼,梳理了壹遍。

在Activity的onResume之後,當前Activity的Window對象中的View會被添加在WindowManager中。

整個View樹的繪圖流程是在 ViewRootImpl 類的 performTraversals() 方法(這個方法巨長)開始的,該方法做的執行過程主要是根據之前設置的狀態,判斷是否重新計算視圖大小 (measure) 、是否重新放置視圖的位置 (layout) 、以及是否重繪 (draw) ,其核心也就是通過判斷來選擇順序執行這三個方法。

ViewRootImpl 調用 performMeasure 執行Window對應的View的測量。

int widthMeasureSpec :他由兩部分組成, 高2位表示MODE ,定義在MeasureSpec類(View的內部類)中,有三種類型, MeasureSpec.EXACTLY 表示確定大小, MeasureSpec.AT_MOST 表示最大大小, MeasureSpec.UNSPECIFIED 不確定。 低30位表示size ,也就是父View的大小。對於系統Window類的DecorVIew對象Mode壹般都為MeasureSpec.EXACTLY ,而size分別對應屏幕寬高。對於子View來說大小是由父View和子View***同決定的。

默認的尺寸大小即傳入的參數都是通過 getDefaultSize 返回的,我們就看壹下該方法的實現。

到此壹次最基礎的元素View的 measure 過程就完成了。

View實際是嵌套的,而且measure是遞歸傳遞的,所以每個View都需要 measure ,能夠嵌套的View都是ViewGroup的子類,所以在ViewGroup中定義了 measureChildren , measureChild , measureChildWithMargins 方法來對子視圖進行測量, measureChildren 內部實質只是循環調用 measureChild , measureChild 和 measureChildWithMargins 的區別就是是否把 margin padding 也作為子視圖的大小。 ViewGroup 本身不調用 measureChildWithMargins 和 measureChildren 方法,由繼承類通過for循環調用此方法進行子View的測量。下面看壹下ViewGroup中稍微復雜的 measureChildWithMargins 方法。

getChildMeasureSpec 的邏輯是通過其父View提供的 MeasureSpec 參數得到 specMode 和 specSize ,然後根據計算出來的 specMode 以及子View的 childDimension (layout_width或layout_height)來計算自身的 measureSpec ,如果其本身包含子視圖,則計算出來的 measureSpec 將作為調用其子視圖 measure 函數的參數,同時也作為自身調用 setMeasuredDimension 的參數,如果其不包含子視圖則默認情況下最終會調用 onMeasure 的默認實現,並最終調用到 setMeasuredDimension 。

Activity 的 onResume 之後,當前 Activity Window 對象中的View(DecorView)會被添加在 WindowManager 中。也就是在 ActivityThread 的 handleResumeActivity 方法中調用 wm.addView(decor, l); 將DecorView添加到 WindowManager 中;

WindowManager 繼承 ViewManager ,它的實現類為 WindowManagerImpl ,該類中的方法的具體實現是由其代理類 WindowManagerGlobal 實現的;

在它的 addView 方法中會創建 ViewRootImpl 的實例,然後將Window對應的View(DecorView),ViewRootImpl,LayoutParams順序添加在WindowManager中,最後將Window所對應的View設置給創建的ViewRootImpl,通過 ViewRootImpl 來更新界面並完成Window的添加過程;

設置view調用的是 ViewRootImpl 的 setView 方法,在該方法中調用 requestLayout(); 方法來異步執行view的繪制方法;之後將 Window 添加到屏幕,通過 WMS (跨進程通信)

在 requestLayout 方法中最終會調用 ViewRootImpl 的 performTraversals(); 方法,該方法做的執行過程主要是根據之前設置的狀態,判斷是否重新計算視圖大小 (measure) 、是否重新放置視圖的位置 (layout) 、以及是否重繪 (draw) ,其核心也就是通過判斷來選擇順序執行這三個方法: performMeasure 、 performLayout 、 performDraw ;

在 performMeasure 方法中調用的是 View 的 measure 方法,該方法是 final 修飾,不能被子類重寫,在該方法中實際調用的是 View 的 onMeasure 方法,子類可以重寫 onMeasure 方法來實現自己的測量規則。

View 默認的 onMeasure 方法很簡單只是調用了 setMeasuredDimension 方法,該方法的作用是給 View 的成員變量 mMeasuredWidth mMeasuredHeight 賦值,View的測量主要就是給這兩個變量賦值,這兩個變量壹旦賦值,也就意味著測量過程的結束。

setMeasuredDimension 方法傳入的尺寸是通過 getDefaultSize(int size, int measureSpec); 方法返回的,在

getDefaultSize 方法中解析 measureSpec Mode Size ,如果Mode為 MeasureSpec.AT_MOST 或者 MeasureSpec.EXACTLY ,最終的size的值為解析後的size;如果 Mode MeasureSpec.UNSPECIFIED ,最終的size為建議的最小值= getSuggestedMinimumWidth ,該方法的具體實現為 return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth()); ,建議的最小寬度和高度都是由View的Background尺寸與通過設置View的 miniXXX 屬性***同決定的

measureSpec 是由 getRootMeasureSpec 方法決定的: measureSpec = View.MeasureSpec.makeMeasureSpec(windowSize, View.MeasureSpec.EXACTLY); 根布局的大小是 Window 的大小,Window大小是不能改變的,總是全屏的。

View實際是嵌套的,而且measure是遞歸傳遞的,所以每個View都需要measure,能夠嵌套的View都是ViewGroup的子類,所以在ViewGroup中定義了 measureChildren , measureChild , measureChildWithMargins 方法來對子視圖進行測量, measureChildren 內部實質只是循環調用 measureChild , measureChild 和 measureChildWithMargins 的區別就是是否把 margin padding 也作為子視圖的大小。

measureChildWithMargins 方法的作用就是對 父View 提供的 measureSpec 參數結合 子View LayoutParams 參數進行了調整,然後再來調用 child.measure() 方法,具體通過方法 getChildMeasureSpec 方法來進行參數調整。計算出來自身的 measureSpec 作為調用其子視圖 measure 方法的參數,同時也作為自身調用 setMeasuredDimension 的參數,如果其不包含子視圖則默認情況下最終會調用 onMeasure 的默認實現,並最終調用到 setMeasuredDimension 。

最終決定 View measure 大小是 View 的 setMeasuredDimension 方法,該方法就是設置mMeasuredWidth和mMeasuredHeight的大小,ViewGroup在 onMeasure 方法調用 setMeasuredDimension 之前調整了 measureSpec

  • 上一篇:怎麽開發分銷app系統壹鍵分享微信朋友圈
  • 下一篇:K線圖怎麽看
  • copyright 2024編程學習大全網