Android O: View的绘制流程(一): 创建和加载
從這篇博客開始,我們會用幾篇文章,?
基于Android O的代碼,分析一下View的繪制流程。
在分析具體的繪制流程前,我們先來了解一下XML中定義的View,?
如何被創建和加載。
一、setContentView?
在分析具體的代碼前,我們先看看Android的視圖結構:?
如上圖所示,每個Activity都與一個Window(具體來說是PhoneWindow)相關聯,后者用于承載實際的用戶界面。?
Window是一個抽象的類,它表示屏幕上用于繪制各種UI元素及響應用戶輸入事件的一個矩形區域。
DecorView是一個應用窗口的根容器,它本質上是一個FrameLayout。?
DecorView僅包含一個子View,它是一個垂直的LinearLayout。?
該LinearLayout包含兩個子元素,一個是TitleView(ActionBar的容器),?
另一個是ContentView(窗口內容的容器)。
ContentView是一個FrameLayout。?
我們在Activity中調用setContentView時,就是在設置它的子View。
現在我們就從Activity的setContentView開始分析,?
看看XML中的View如何被一步步創建和加載。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
從代碼來看,Activity的setContentView函數,會先加載自己的ContentView,?
然后再利用initWindowDecorActionBar函數加載ActionBar。
我們主要跟進一下getWindow().setContentView函數,?
即PhoneWindow中的setContentView函數。
對應的代碼如下:
public void setContentView(int layoutResID) {if (mContentParent == null) {// mContentParent為ContentView的父容器, 即DecorView// 若為空, 則調用installDecor()生成installDecor();} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {// FEATURE_CONTENT_TRANSITIONS的用途可參考其注釋// 主要是支持動畫切換Activity的content view// mContentParent不為null, 則移除DecorView的所有子ViewmContentParent.removeAllViews();}if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,getContext());//轉移到新的content viewtransitionTo(newScene);} else {//默認在此處填充布局, 將我們在xml中定義的contentView加載到mContentParent中mLayoutInflater.inflate(layoutResID, mContentParent);}.............final Callback cb = getCallback();if (cb != null && !isDestroyed()) {// 回調onContentChanged(), 通知Activity窗口內容發生了改變cb.onContentChanged();}......... }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
從上面的代碼可以看出,在setContentView函數中,?
主要是通過LayoutInflater的inflate函數,來解析XML文件并填充ContentView。
二、inflate?
我們一起來跟進一下對應的源碼:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
從上面的代碼可以看出,當XML以merge標簽開始時,?
將直接調用rInflate函數來進行處理。
否則,正常情況下,會先創建出XML的根View,?
然后利用rInflateChildren加載根View的子View。?
最終,根據條件選擇是否將根View attach到父容器中。
三、rInflate?
我們先來看看rInflate對應的代碼:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
從上面代碼可以看出,rInflate函數負責解析當前區域的XML,?
然后創建出這段區域的根View,并利用rInflateChildren加載子View,?
最終將根View填充到父容器內。
我們來看看rInflateChildren的代碼:
//容易看出,實際上還是調用了rInflate函數//即將傳入的View作為新的根View進行加載final void rInflateChildren(XmlPullParser parser, View parent, AttributeSet attrs,boolean finishInflate) throws XmlPullParserException, IOException {rInflate(parser, parent, parent.getContext(), attrs, finishInflate);}- 1
- 2
- 3
- 4
- 5
- 6
至此,Activity加載界面的流程分析完畢。
四、總結?
?
從代碼的角度來看,Activity加載界面的流程是比較好理解的。?
整體的思想實際上可以簡化為上圖,其中最重要的函數是rInflate。?
通過該函數,系統會以先序遍歷的方式,創建XML對應的View。
簡單來講,創建View的方式類似于:?
假設一段XML范圍內,定義了根View及1~n個子View,?
那么創建出根View后,必須先加載View 1及其所有子View,?
才會開始加載View 2。
總結
以上是生活随笔為你收集整理的Android O: View的绘制流程(一): 创建和加载的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android Treble 计划技术文
- 下一篇: Android O: View的绘制流程