Android之View繪制流程開胃菜---setContentView(...)詳細分析


版權聲明:本文出自汪磊的博客,轉載請務必注明出處。

1 為什么要分析setContentView方法

作為安卓開發者相信大部分都有意或者無意看過如下圖示:PhoneWindow,DecorView這些究竟都是些神馬玩意?圖示的層級關系是怎么來的?我們自己所寫的布局是怎么加載到窗體上的?以及在上一篇《Android事件傳遞機制詳解及最新源碼分析——Activity篇》中提到過我們在調用setContentView設置布局的時候其實都是被放置在id為content的FrameLayout 布局中的,這里又是什么鬼?帶着這些問題我們一起探討下setContentView方法究竟做了些什么。

2 分析setContentView方法(API23)

我們平時調用setContentView,例如:setContentView(R.layout.xxx);點進源碼都是先調用Activity中的setContentView方法,我們就從Activity中的setContentView方法開始分析。

Activity的源碼中有三個重載的setContentView方法,如下:

 1 public void setContentView(@LayoutRes int layoutResID) {  2  getWindow().setContentView(layoutResID);  3  initWindowDecorActionBar();  4  }  5 
 6 public void setContentView(View view) {  7  getWindow().setContentView(view);  8  initWindowDecorActionBar();  9  } 10 
11 public void setContentView(View view, ViewGroup.LayoutParams params) { 12  getWindow().setContentView(view, params); 13  initWindowDecorActionBar(); 14     } 

可以看到三個方法都是又調用了getWindow().setContentView(...);在上一篇文章中分析過getWindow()返回mWindow對象,mWindow定義是Windo類型,實際初始化的時候初始化為PhoneWindow,源碼如下:

private Window mWindow; mWindow = new PhoneWindow(this);

這里說明一下:Window 是抽象類,主要提供一些繪制窗口的一些公用方法,PhoneWindow是Window的具體繼承實現類。

我們看看Window類中setContentView方法,源碼如下:

public abstract void setContentView(@LayoutRes int layoutResID); public abstract void setContentView(View view); public abstract void setContentView(View view, ViewGroup.LayoutParams params);

看到了吧,這里只是三個抽象方法而已,具體邏輯需要子類自己去實現。

接下來,我們就就去PhoneWindow中找一下吧,源碼如下:

 @Override public void setContentView(int layoutResID) { // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window // decor, when theme attributes and the like are crystalized. Do not check the feature // before this happens.
        if (mContentParent == null) { installDecor(); } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) { mContentParent.removeAllViews(); } if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) { final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID, getContext()); transitionTo(newScene); } else { mLayoutInflater.inflate(layoutResID, mContentParent); } mContentParent.requestApplyInsets(); final Callback cb = getCallback(); if (cb != null && !isDestroyed()) { cb.onContentChanged(); } } @Override public void setContentView(View view) { setContentView(view, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); } @Override public void setContentView(View view, ViewGroup.LayoutParams params) { // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window // decor, when theme attributes and the like are crystalized. Do not check the feature // before this happens.
        if (mContentParent == null) { installDecor(); } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) { mContentParent.removeAllViews(); } if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) { view.setLayoutParams(params); final Scene newScene = new Scene(mContentParent, view); transitionTo(newScene); } else { mContentParent.addView(view, params); } mContentParent.requestApplyInsets(); final Callback cb = getCallback(); if (cb != null && !isDestroyed()) { cb.onContentChanged(); } }

 看到了吧,在子類PhoneWindow中有具體實現,並且setContentView(View view)實際上也是調用的setContentView(View view, ViewGroup.LayoutParams params),只不過params參數默認傳入為MATCH_PARENT。並且setContentView(int layoutResID)與setContentView(View view, ViewGroup.LayoutParams params)方法代碼邏輯是一樣的,這里我們選取setContentView(int layoutResID)方法加以分析即可。

到這里我們明白平時調用的setContentView(R.layout.xxx)方法實際上調用的是PhoneWindow中的setContentView(int layoutResID)方法,接下來我們着重分析此方法。

3 分析PhoneWindow中的setContentView(int layoutResID)方法(API23)

源碼如下:

 1  @Override  2     public void setContentView(int layoutResID) {  3         // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window  4         // decor, when theme attributes and the like are crystalized. Do not check the feature  5         // before this happens.
 6         if (mContentParent == null) {  7  installDecor();  8         } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {  9  mContentParent.removeAllViews(); 10  } 11 
12         if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) { 13             final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID, 14  getContext()); 15  transitionTo(newScene); 16         } else { 17  mLayoutInflater.inflate(layoutResID, mContentParent); 18  } 19  mContentParent.requestApplyInsets(); 20         final Callback cb = getCallback(); 21         if (cb != null && !isDestroyed()) { 22  cb.onContentChanged(); 23  } 24     }

 第6行代碼判斷mContentParent 是否為空,mContentParent PhoneWindow中定義的一個ViewGroup類型實例。第一次運行的時候mContentParent 為null,則進入判斷執行第7行代碼

installDecor(),我們看看installDecor()方法都做了什么源碼如下:這里只列出主要代碼

 1 private void installDecor() {  2         if (mDecor == null) {  3             mDecor = generateDecor();  4  ...  5  }  6         if (mContentParent == null) {  7             mContentParent = generateLayout(mDecor);  8  ...  9  } 10  .... 11 }        

第2行代碼判斷mDecor是否為null,為null則執行generateDecor()代碼並對mDecor賦值,mDecor是DecorView的一個實例,DecorView是PhoneWindow的內部類,定義如下:

private final class DecorView extends FrameLayout implements RootViewSurfaceTaker

 看到了吧,DecorView其實就是FrameLayout 的子類,對FrameLayout 進行裝飾,增強其某些功能。

我們繼續看generateDecor()源碼:

1    protected DecorView generateDecor() { 2         return new DecorView(getContext(), -1); 3     }

很簡單吧就是生成DecorView對象並且返回,這里沒什么要多說的。

返回installDecor()方法我們繼續向下分析。

第6行代碼又是判斷mContentParent 是否為null,是則執行generateLayout(mDecor)方法並將返回值賦值給mContentParent

那我們就繼續看generateLayout(mDecor)源碼:

 1 protected ViewGroup generateLayout(DecorView decor) {  2         // Apply data from current theme.
 3 
 4         TypedArray a = getWindowStyle();  5 
 6         if (false) {  7             System.out.println("From style:");  8             String s = "Attrs:";  9             for (int i = 0; i < R.styleable.Window.length; i++) {  10                 s = s + " " + Integer.toHexString(R.styleable.Window[i]) + "="
 11                         + a.getString(i);  12  }  13  System.out.println(s);  14  }  15 
 16         mIsFloating = a.getBoolean(R.styleable.Window_windowIsFloating, false);  17         int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR)  18                 & (~getForcedWindowFlags());  19         if (mIsFloating) {  20  setLayout(WRAP_CONTENT, WRAP_CONTENT);  21             setFlags(0, flagsToUpdate);  22         } else {  23             setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate);  24  }  25 
 26         if (a.getBoolean(R.styleable.Window_windowNoTitle, false)) {  27  requestFeature(FEATURE_NO_TITLE);  28         } else if (a.getBoolean(R.styleable.Window_windowActionBar, false)) {  29             // Don't allow an action bar if there is no title.
 30  requestFeature(FEATURE_ACTION_BAR);  31  }  32 
 33         if (a.getBoolean(R.styleable.Window_windowActionBarOverlay, false)) {  34  requestFeature(FEATURE_ACTION_BAR_OVERLAY);  35  }  36 
 37         if (a.getBoolean(R.styleable.Window_windowActionModeOverlay, false)) {  38  requestFeature(FEATURE_ACTION_MODE_OVERLAY);  39  }  40 
 41         if (a.getBoolean(R.styleable.Window_windowSwipeToDismiss, false)) {  42  requestFeature(FEATURE_SWIPE_TO_DISMISS);  43  }  44 
 45         if (a.getBoolean(R.styleable.Window_windowFullscreen, false)) {  46             setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN & (~getForcedWindowFlags()));  47  }  48 
 49         if (a.getBoolean(R.styleable.Window_windowTranslucentStatus,  50                 false)) {  51  setFlags(FLAG_TRANSLUCENT_STATUS, FLAG_TRANSLUCENT_STATUS  52                     & (~getForcedWindowFlags()));  53  }  54 
 55         if (a.getBoolean(R.styleable.Window_windowTranslucentNavigation,  56                 false)) {  57  setFlags(FLAG_TRANSLUCENT_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION  58                     & (~getForcedWindowFlags()));  59  }  60 
 61         if (a.getBoolean(R.styleable.Window_windowOverscan, false)) {  62             setFlags(FLAG_LAYOUT_IN_OVERSCAN, FLAG_LAYOUT_IN_OVERSCAN&(~getForcedWindowFlags()));  63  }  64 
 65         if (a.getBoolean(R.styleable.Window_windowShowWallpaper, false)) {  66             setFlags(FLAG_SHOW_WALLPAPER, FLAG_SHOW_WALLPAPER&(~getForcedWindowFlags()));  67  }  68 
 69         if (a.getBoolean(R.styleable.Window_windowEnableSplitTouch,  70  getContext().getApplicationInfo().targetSdkVersion  71                         >= android.os.Build.VERSION_CODES.HONEYCOMB)) {  72             setFlags(FLAG_SPLIT_TOUCH, FLAG_SPLIT_TOUCH&(~getForcedWindowFlags()));  73  }  74 
 75  a.getValue(R.styleable.Window_windowMinWidthMajor, mMinWidthMajor);  76  a.getValue(R.styleable.Window_windowMinWidthMinor, mMinWidthMinor);  77         if (a.hasValue(R.styleable.Window_windowFixedWidthMajor)) {  78             if (mFixedWidthMajor == null) mFixedWidthMajor = new TypedValue();  79  a.getValue(R.styleable.Window_windowFixedWidthMajor,  80  mFixedWidthMajor);  81  }  82         if (a.hasValue(R.styleable.Window_windowFixedWidthMinor)) {  83             if (mFixedWidthMinor == null) mFixedWidthMinor = new TypedValue();  84  a.getValue(R.styleable.Window_windowFixedWidthMinor,  85  mFixedWidthMinor);  86  }  87         if (a.hasValue(R.styleable.Window_windowFixedHeightMajor)) {  88             if (mFixedHeightMajor == null) mFixedHeightMajor = new TypedValue();  89  a.getValue(R.styleable.Window_windowFixedHeightMajor,  90  mFixedHeightMajor);  91  }  92         if (a.hasValue(R.styleable.Window_windowFixedHeightMinor)) {  93             if (mFixedHeightMinor == null) mFixedHeightMinor = new TypedValue();  94  a.getValue(R.styleable.Window_windowFixedHeightMinor,  95  mFixedHeightMinor);  96  }  97         if (a.getBoolean(R.styleable.Window_windowContentTransitions, false)) {  98  requestFeature(FEATURE_CONTENT_TRANSITIONS);  99  } 100         if (a.getBoolean(R.styleable.Window_windowActivityTransitions, false)) { 101  requestFeature(FEATURE_ACTIVITY_TRANSITIONS); 102  } 103 
104         final Context context = getContext(); 105         final int targetSdk = context.getApplicationInfo().targetSdkVersion; 106         final boolean targetPreHoneycomb = targetSdk < android.os.Build.VERSION_CODES.HONEYCOMB; 107         final boolean targetPreIcs = targetSdk < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH; 108         final boolean targetPreL = targetSdk < android.os.Build.VERSION_CODES.LOLLIPOP; 109         final boolean targetHcNeedsOptions = context.getResources().getBoolean( 110  R.bool.target_honeycomb_needs_options_menu); 111         final boolean noActionBar = !hasFeature(FEATURE_ACTION_BAR) || hasFeature(FEATURE_NO_TITLE); 112 
113         if (targetPreHoneycomb || (targetPreIcs && targetHcNeedsOptions && noActionBar)) { 114  setNeedsMenuKey(WindowManager.LayoutParams.NEEDS_MENU_SET_TRUE); 115         } else { 116  setNeedsMenuKey(WindowManager.LayoutParams.NEEDS_MENU_SET_FALSE); 117  } 118 
119         // Non-floating windows on high end devices must put up decor beneath the system bars and 120         // therefore must know about visibility changes of those.
121         if (!mIsFloating && ActivityManager.isHighEndGfx()) { 122             if (!targetPreL && a.getBoolean( 123  R.styleable.Window_windowDrawsSystemBarBackgrounds, 124                     false)) { 125  setFlags(FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, 126                         FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS & ~getForcedWindowFlags()); 127  } 128  } 129         if (!mForcedStatusBarColor) { 130             mStatusBarColor = a.getColor(R.styleable.Window_statusBarColor, 0xFF000000); 131  } 132         if (!mForcedNavigationBarColor) { 133             mNavigationBarColor = a.getColor(R.styleable.Window_navigationBarColor, 0xFF000000); 134  } 135         if (a.getBoolean(R.styleable.Window_windowLightStatusBar, false)) { 136  decor.setSystemUiVisibility( 137                     decor.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); 138  } 139 
140         if (mAlwaysReadCloseOnTouchAttr || getContext().getApplicationInfo().targetSdkVersion 141                 >= android.os.Build.VERSION_CODES.HONEYCOMB) { 142             if (a.getBoolean( 143  R.styleable.Window_windowCloseOnTouchOutside, 144                     false)) { 145                 setCloseOnTouchOutsideIfNotSet(true); 146  } 147  } 148 
149         WindowManager.LayoutParams params = getAttributes(); 150 
151         if (!hasSoftInputMode()) { 152             params.softInputMode = a.getInt( 153  R.styleable.Window_windowSoftInputMode, 154  params.softInputMode); 155  } 156 
157         if (a.getBoolean(R.styleable.Window_backgroundDimEnabled, 158  mIsFloating)) { 159             /* All dialogs should have the window dimmed */
160             if ((getForcedWindowFlags()&WindowManager.LayoutParams.FLAG_DIM_BEHIND) == 0) { 161                 params.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND; 162  } 163             if (!haveDimAmount()) { 164                 params.dimAmount = a.getFloat( 165                         android.R.styleable.Window_backgroundDimAmount, 0.5f); 166  } 167  } 168 
169         if (params.windowAnimations == 0) { 170             params.windowAnimations = a.getResourceId( 171                     R.styleable.Window_windowAnimationStyle, 0); 172  } 173 
174         // The rest are only done if this window is not embedded; otherwise, 175         // the values are inherited from our container.
176         if (getContainer() == null) { 177             if (mBackgroundDrawable == null) { 178                 if (mBackgroundResource == 0) { 179                     mBackgroundResource = a.getResourceId( 180                             R.styleable.Window_windowBackground, 0); 181  } 182                 if (mFrameResource == 0) { 183                     mFrameResource = a.getResourceId(R.styleable.Window_windowFrame, 0); 184  } 185                 mBackgroundFallbackResource = a.getResourceId( 186                         R.styleable.Window_windowBackgroundFallback, 0); 187                 if (false) { 188                     System.out.println("Background: "
189                             + Integer.toHexString(mBackgroundResource) + " Frame: "
190                             + Integer.toHexString(mFrameResource)); 191  } 192  } 193             mElevation = a.getDimension(R.styleable.Window_windowElevation, 0); 194             mClipToOutline = a.getBoolean(R.styleable.Window_windowClipToOutline, false); 195             mTextColor = a.getColor(R.styleable.Window_textColor, Color.TRANSPARENT); 196  } 197 
198         // Inflate the window decor.
199 
200         int layoutResource; 201         int features = getLocalFeatures(); 202         // System.out.println("Features: 0x" + Integer.toHexString(features));
203         if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) { 204             layoutResource = R.layout.screen_swipe_dismiss; 205         } else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) { 206             if (mIsFloating) { 207                 TypedValue res = new TypedValue(); 208  getContext().getTheme().resolveAttribute( 209                         R.attr.dialogTitleIconsDecorLayout, res, true); 210                 layoutResource = res.resourceId; 211             } else { 212                 layoutResource = R.layout.screen_title_icons; 213  } 214             // XXX Remove this once action bar supports these features.
215  removeFeature(FEATURE_ACTION_BAR); 216             // System.out.println("Title Icons!");
217         } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
218                 && (features & (1 << FEATURE_ACTION_BAR)) == 0) { 219             // Special case for a window with only a progress bar (and title). 220             // XXX Need to have a no-title version of embedded windows.
221             layoutResource = R.layout.screen_progress; 222             // System.out.println("Progress!");
223         } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) { 224             // Special case for a window with a custom title. 225             // If the window is floating, we need a dialog layout
226             if (mIsFloating) { 227                 TypedValue res = new TypedValue(); 228  getContext().getTheme().resolveAttribute( 229                         R.attr.dialogCustomTitleDecorLayout, res, true); 230                 layoutResource = res.resourceId; 231             } else { 232                 layoutResource = R.layout.screen_custom_title; 233  } 234             // XXX Remove this once action bar supports these features.
235  removeFeature(FEATURE_ACTION_BAR); 236         } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) { 237             // If no other features and not embedded, only need a title. 238             // If the window is floating, we need a dialog layout
239             if (mIsFloating) { 240                 TypedValue res = new TypedValue(); 241  getContext().getTheme().resolveAttribute( 242                         R.attr.dialogTitleDecorLayout, res, true); 243                 layoutResource = res.resourceId; 244             } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) { 245                 layoutResource = a.getResourceId( 246  R.styleable.Window_windowActionBarFullscreenDecorLayout, 247  R.layout.screen_action_bar); 248             } else { 249                 layoutResource = R.layout.screen_title; 250  } 251             // System.out.println("Title!");
252         } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) { 253             layoutResource = R.layout.screen_simple_overlay_action_mode; 254         } else { 255             // Embedded, so no decoration is needed.
256             layoutResource = R.layout.screen_simple; 257             // System.out.println("Simple!");
258  } 259 
260  mDecor.startChanging(); 261 
262         View in = mLayoutInflater.inflate(layoutResource, null); 263         decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); 264         mContentRoot = (ViewGroup) in; 265 
266         ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); 267         if (contentParent == null) { 268             throw new RuntimeException("Window couldn't find content container view"); 269  } 270 
271         if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) { 272             ProgressBar progress = getCircularProgressBar(false); 273             if (progress != null) { 274                 progress.setIndeterminate(true); 275  } 276  } 277 
278         if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) { 279  registerSwipeCallbacks(); 280  } 281 
282         // Remaining setup -- of background and title -- that only applies 283         // to top-level windows.
284         if (getContainer() == null) { 285             final Drawable background; 286             if (mBackgroundResource != 0) { 287                 background = getContext().getDrawable(mBackgroundResource); 288             } else { 289                 background = mBackgroundDrawable; 290  } 291  mDecor.setWindowBackground(background); 292 
293             final Drawable frame; 294             if (mFrameResource != 0) { 295                 frame = getContext().getDrawable(mFrameResource); 296             } else { 297                 frame = null; 298  } 299  mDecor.setWindowFrame(frame); 300 
301  mDecor.setElevation(mElevation); 302  mDecor.setClipToOutline(mClipToOutline); 303 
304             if (mTitle != null) { 305  setTitle(mTitle); 306  } 307 
308             if (mTitleColor == 0) { 309                 mTitleColor = mTextColor; 310  } 311  setTitleColor(mTitleColor); 312  } 313 
314  mDecor.finishChanging(); 315 
316         return contentParent; 317     }

 我勒個去,這方法太挺長,不過別擔心,總體邏輯不復雜。

 第4行代碼getWindowStyle()是什么鬼呢?這里就直接說了,我們在manifest文件配置的Activity的時候有時會指定theme,如:android:theme="@style/AppTheme",getWindowStyle()就是獲取我們配置的theme信息。

接着6-199行代碼都是根據我們通過getWindowStyle()獲取的theme配置信息進行相應設置。

200行代碼,定義layoutResource變量。

201調用getLocalFeatures()方法又是干什么呢?我們有時會通過代碼對Activity設置一些Feature,如:requestWindowFeature(Window.FEATURE_NO_TITLE);這里getLocalFeatures()方法就是獲取通過requestWindowFeature設置的一些值。

202-258根據獲取的features不同對layoutResource進行不同的賦值,layoutResource主要紀錄不同的布局文件。如果什么也沒設置,也就是說Activity沒有任何修飾,那么就賦值為

R.layout.screen_simple,我們看一下R.layout.screen_simple布局源碼:

 1 <?xml version="1.0" encoding="utf-8"?>
 2 
 3 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 4     android:layout_width="match_parent"
 5     android:layout_height="match_parent"
 6     android:fitsSystemWindows="true"
 7     android:orientation="vertical">
 8     <ViewStub android:id="@+id/action_mode_bar_stub"
 9               android:inflatedId="@+id/action_mode_bar"
10               android:layout="@layout/action_mode_bar"
11               android:layout_width="match_parent"
12               android:layout_height="wrap_content"
13               android:theme="?attr/actionBarTheme" />
14     <FrameLayout 15          android:id="@android:id/content"
16          android:layout_width="match_parent"
17          android:layout_height="match_parent"
18          android:foregroundInsidePadding="false"
19          android:foregroundGravity="fill_horizontal|top"
20          android:foreground="?android:attr/windowContentOverlay" />
21 </LinearLayout>

 看到了吧,很簡單,就包括一個actiob_Bar,還有一個id為content的FrameLayout,並且action_Bar部分使用了布局優化ViewStub 。

繼續向下分析262行將layoutResource記錄的布局轉化為View。

263行代碼將262行生成的view添加到decor中,這個decor就是我們上面分析過的mDecor。

264行將262行生成的View賦值給mContentRoot,用以紀錄。

接下來266行通過findViewById找到ID為ID_ANDROID_CONTENT的View,這個ID_ANDROID_CONTENT又是什么鬼?通過查找最終在父類Window中找到,源碼如下:

    public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;

 看到了吧,就是id為content的View,以R.layout.screen_simple布局為例,最終找的就是id為content的FrameLayout。賦值給名為contentParent的ViewGroup。

最終在316行將contentParent作為generateLayout方法的返回值返回。到此generateLayout想要探討的就都探討完了。

我們馬上回看上面分析的installDecor()方法第7行。將generateLayout方法返回值賦值給mContentParent,到這里,你應該知道mContentParent就是DecorView中布局為content的部分。

我們在回看一開始分析的setContentView方法,之前分析到第7行,繼續向下看直到第17行,調用mLayoutInflater.inflate(layoutResID, mContentParent),至於inflate方法內部邏輯這里就不分析了,不是本文重點,直接說結論:mLayoutInflater.inflate(layoutResID, mContentParent)就是將layoutResID布局轉化為View添加到mContentParent中。還記得mContentParent嗎?它就是DecorView中id為content的View。到這里就知道了原來我們自己定義的布局最終都是加載到這里了。

4總結

經過上面分析相信你已經有了一些眉目,我們趕緊總結一下。

我們平時在Activity中調用的setContentView方法其實都是調用的PhoneWindow中的setContentView方法,其首先會判斷mContentParent是否為null,如果為null,則執行installDecor()方法,在installDecor()方法中會對mDecor進行判斷是否為null,為null則進行初始化,mDecor為DecorView類型,DecorView繼承自FrameLayout。接下來繼續判斷mContentParent是否為null,為null則執行generateLayout方法,在generateLayout方法中最重要的邏輯就是根據我們設置的不同feature找到對應布局文件,並且inflate為View,通過addView方法加入到mDecor中,然后找到布局文件中ID為content的View作為generateLayout方法最終返回值返回。接下來回到installDecor方法將generateLayout返回值賦值給mContentParent,最后回到setContentView,將我們自己的布局文件layoutResID加載到mContentParent中。

相信經過上述分析你應該對本文一開始的那張圖會有更深刻的認識。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM