Android之View绘制流程开胃菜---setContentView(...)详细分析
版權(quán)聲明:本文出自汪磊的博客,轉(zhuǎn)載請(qǐng)務(wù)必注明出處。
1 為什么要分析setContentView方法
作為安卓開(kāi)發(fā)者相信大部分都有意或者無(wú)意看過(guò)如下圖示:PhoneWindow,DecorView這些究竟都是些神馬玩意?圖示的層級(jí)關(guān)系是怎么來(lái)的?我們自己所寫(xiě)的布局是怎么加載到窗體上的?以及在上一篇《Android事件傳遞機(jī)制詳解及最新源碼分析——Activity篇》中提到過(guò)我們?cè)谡{(diào)用setContentView設(shè)置布局的時(shí)候其實(shí)都是被放置在id為content的FrameLayout 布局中的,這里又是什么鬼?帶著這些問(wèn)題我們一起探討下setContentView方法究竟做了些什么。
2 分析setContentView方法(API23)
我們平時(shí)調(diào)用setContentView,例如:setContentView(R.layout.xxx);點(diǎn)進(jìn)源碼都是先調(diào)用Activity中的setContentView方法,我們就從Activity中的setContentView方法開(kāi)始分析。
Activity的源碼中有三個(gè)重載的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 }可以看到三個(gè)方法都是又調(diào)用了getWindow().setContentView(...);在上一篇文章中分析過(guò)getWindow()返回mWindow對(duì)象,mWindow定義是Windo類(lèi)型,實(shí)際初始化的時(shí)候初始化為PhoneWindow,源碼如下:
private Window mWindow;mWindow = new PhoneWindow(this);這里說(shuō)明一下:Window 是抽象類(lèi),主要提供一些繪制窗口的一些公用方法,PhoneWindow是Window的具體繼承實(shí)現(xiàn)類(lèi)。
我們看看Window類(lèi)中setContentView方法,源碼如下:
public abstract void setContentView(@LayoutRes int layoutResID);public abstract void setContentView(View view);public abstract void setContentView(View view, ViewGroup.LayoutParams params);看到了吧,這里只是三個(gè)抽象方法而已,具體邏輯需要子類(lèi)自己去實(shí)現(xiàn)。
接下來(lái),我們就就去PhoneWindow中找一下吧,源碼如下:
@Overridepublic 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();}}@Overridepublic void setContentView(View view) {setContentView(view, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));}@Overridepublic 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();}}?看到了吧,在子類(lèi)PhoneWindow中有具體實(shí)現(xiàn),并且setContentView(View view)實(shí)際上也是調(diào)用的setContentView(View view, ViewGroup.LayoutParams params),只不過(guò)params參數(shù)默認(rèn)傳入為MATCH_PARENT。并且setContentView(int layoutResID)與setContentView(View view, ViewGroup.LayoutParams params)方法代碼邏輯是一樣的,這里我們選取setContentView(int layoutResID)方法加以分析即可。
到這里我們明白平時(shí)調(diào)用的setContentView(R.layout.xxx)方法實(shí)際上調(diào)用的是PhoneWindow中的setContentView(int layoutResID)方法,接下來(lái)我們著重分析此方法。
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中定義的一個(gè)ViewGroup類(lèi)型實(shí)例。第一次運(yùn)行的時(shí)候mContentParent 為null,則進(jìn)入判斷執(zhí)行第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則執(zhí)行generateDecor()代碼并對(duì)mDecor賦值,mDecor是DecorView的一個(gè)實(shí)例,DecorView是PhoneWindow的內(nèi)部類(lèi),定義如下:
private final class DecorView extends FrameLayout implements RootViewSurfaceTaker?看到了吧,DecorView其實(shí)就是FrameLayout 的子類(lèi),對(duì)FrameLayout 進(jìn)行裝飾,增強(qiáng)其某些功能。
我們繼續(xù)看generateDecor()源碼:
1 protected DecorView generateDecor() { 2 return new DecorView(getContext(), -1); 3 }很簡(jiǎn)單吧就是生成DecorView對(duì)象并且返回,這里沒(méi)什么要多說(shuō)的。
返回installDecor()方法我們繼續(xù)向下分析。
第6行代碼又是判斷mContentParent 是否為null,是則執(zhí)行generateLayout(mDecor)方法并將返回值賦值給mContentParent 。
那我們就繼續(xù)看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 }?我勒個(gè)去,這方法太挺長(zhǎng),不過(guò)別擔(dān)心,總體邏輯不復(fù)雜。
?第4行代碼getWindowStyle()是什么鬼呢?這里就直接說(shuō)了,我們?cè)趍anifest文件配置的Activity的時(shí)候有時(shí)會(huì)指定theme,如:android:theme="@style/AppTheme",getWindowStyle()就是獲取我們配置的theme信息。
接著6-199行代碼都是根據(jù)我們通過(guò)getWindowStyle()獲取的theme配置信息進(jìn)行相應(yīng)設(shè)置。
200行代碼,定義layoutResource變量。
201調(diào)用getLocalFeatures()方法又是干什么呢?我們有時(shí)會(huì)通過(guò)代碼對(duì)Activity設(shè)置一些Feature,如:requestWindowFeature(Window.FEATURE_NO_TITLE);這里getLocalFeatures()方法就是獲取通過(guò)requestWindowFeature設(shè)置的一些值。
202-258根據(jù)獲取的features不同對(duì)layoutResource進(jìn)行不同的賦值,layoutResource主要紀(jì)錄不同的布局文件。如果什么也沒(méi)設(shè)置,也就是說(shuō)Activity沒(méi)有任何修飾,那么就賦值為
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>?看到了吧,很簡(jiǎn)單,就包括一個(gè)actiob_Bar,還有一個(gè)id為content的FrameLayout,并且action_Bar部分使用了布局優(yōu)化ViewStub 。
繼續(xù)向下分析262行將layoutResource記錄的布局轉(zhuǎn)化為View。
263行代碼將262行生成的view添加到decor中,這個(gè)decor就是我們上面分析過(guò)的mDecor。
264行將262行生成的View賦值給mContentRoot,用以紀(jì)錄。
接下來(lái)266行通過(guò)findViewById找到ID為ID_ANDROID_CONTENT的View,這個(gè)ID_ANDROID_CONTENT又是什么鬼?通過(guò)查找最終在父類(lèi)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,到這里,你應(yīng)該知道mContentParent就是DecorView中布局為content的部分。
我們?cè)诨乜匆婚_(kāi)始分析的setContentView方法,之前分析到第7行,繼續(xù)向下看直到第17行,調(diào)用mLayoutInflater.inflate(layoutResID, mContentParent),至于inflate方法內(nèi)部邏輯這里就不分析了,不是本文重點(diǎn),直接說(shuō)結(jié)論:mLayoutInflater.inflate(layoutResID, mContentParent)就是將layoutResID布局轉(zhuǎn)化為View添加到mContentParent中。還記得mContentParent嗎?它就是DecorView中id為content的View。到這里就知道了原來(lái)我們自己定義的布局最終都是加載到這里了。
4總結(jié)
經(jīng)過(guò)上面分析相信你已經(jīng)有了一些眉目,我們趕緊總結(jié)一下。
我們平時(shí)在Activity中調(diào)用的setContentView方法其實(shí)都是調(diào)用的PhoneWindow中的setContentView方法,其首先會(huì)判斷mContentParent是否為null,如果為null,則執(zhí)行installDecor()方法,在installDecor()方法中會(huì)對(duì)mDecor進(jìn)行判斷是否為null,為null則進(jìn)行初始化,mDecor為DecorView類(lèi)型,DecorView繼承自FrameLayout。接下來(lái)繼續(xù)判斷mContentParent是否為null,為null則執(zhí)行g(shù)enerateLayout方法,在generateLayout方法中最重要的邏輯就是根據(jù)我們?cè)O(shè)置的不同feature找到對(duì)應(yīng)布局文件,并且inflate為View,通過(guò)addView方法加入到mDecor中,然后找到布局文件中ID為content的View作為generateLayout方法最終返回值返回。接下來(lái)回到installDecor方法將generateLayout返回值賦值給mContentParent,最后回到setContentView,將我們自己的布局文件layoutResID加載到mContentParent中。
相信經(jīng)過(guò)上述分析你應(yīng)該對(duì)本文一開(kāi)始的那張圖會(huì)有更深刻的認(rèn)識(shí)。
?
轉(zhuǎn)載于:https://www.cnblogs.com/leipDao/p/7509222.html
總結(jié)
以上是生活随笔為你收集整理的Android之View绘制流程开胃菜---setContentView(...)详细分析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 数字IC验证学习(一)
- 下一篇: 财务人员福音,财务收支报告模板