我們都知道在Activity中通過setContentView(layoutId)
來加載布局文件流妻,使我們的布局文件能夠顯示在手機(jī)上,那么系統(tǒng)是如何將我們的布局文件加載到界面上的呢?
setContentView
// Activity.java
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
通過setContentView
的源碼可以發(fā)現(xiàn)其實(shí)是調(diào)用了Window的setContentView方法尚蝌,而Window是一個抽象類遭庶,PhoneWindow是Window的實(shí)現(xiàn)類。
// PhoneWindow.java
@Override
public void setContentView(int layoutResID) {
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();
}
mContentParentExplicitlySet = true;
}
代碼中首先通過installDecor()
創(chuàng)建出DecorView蓖乘。
之后再通過mLayoutInflater.inflate(layoutResID, mContentParent)
將我們的布局加載進(jìn)內(nèi)存锤悄。
DecorView
private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {
// 創(chuàng)建DecorView
mDecor = generateDecor(-1);
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
} else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
// 將android.R.id.content解析出來
mContentParent = generateLayout(mDecor);
}
}
installDecor
主要有2個作用:
-
創(chuàng)建DeorView
protected DecorView generateDecor(int featureId) { Context context; if (mUseDecorContext) { Context applicationContext = getContext().getApplicationContext(); if (applicationContext == null) { context = getContext(); } else { context = new DecorContext(applicationContext, getContext().getResources()); if (mTheme != -1) { context.setTheme(mTheme); } } } else { context = getContext(); } return new DecorView(context, featureId, this, getAttributes()); }
-
將android.R.id.content解析出來
protected ViewGroup generateLayout(DecorView decor) { // Inflate the window decor. int layoutResource; int features = getLocalFeatures(); // 各種判斷加載系統(tǒng)布局 if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) { layoutResource = R.layout.screen_swipe_dismiss; } else { layoutResource = R.layout.screen_simple; } mDecor.startChanging(); // 將得到的布局文件加載到DecorView中 mDecor.onResourcesLoaded(mLayoutInflater, layoutResource); // 獲取android.R.id.content ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); }
generateLayout
的作用就是:-
通過條件獲取到系統(tǒng)需要加載的布局文件,這里我們以最簡單的
R.layout.screen_simple
來看:<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" android:orientation="vertical"> <ViewStub android:id="@+id/action_mode_bar_stub" android:inflatedId="@+id/action_mode_bar" android:layout="@layout/action_mode_bar" android:layout_width="match_parent" android:layout_height="wrap_content" android:theme="?attr/actionBarTheme" /> <FrameLayout android:id="@android:id/content" android:layout_width="match_parent" android:layout_height="match_parent" android:foregroundInsidePadding="false" android:foregroundGravity="fill_horizontal|top" android:foreground="?android:attr/windowContentOverlay" /> </LinearLayout>
-
將得到的布局文件加載到DecorView中:
void onResourcesLoaded(LayoutInflater inflater, int layoutResource) { mDecorCaptionView = createDecorCaptionView(inflater); final View root = inflater.inflate(layoutResource, null); if (mDecorCaptionView != null) { if (mDecorCaptionView.getParent() == null) { addView(mDecorCaptionView, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); } mDecorCaptionView.addView(root, new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT)); } else { // Put it below the color views. addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); } mContentRoot = (ViewGroup) root; initializeElevation();
}
```-
獲取android.R.id.content
public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content; ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
至此系統(tǒng)的布局就加載好了嘉抒,大概的結(jié)構(gòu)如下:
-
inflate加載我們的布局
當(dāng)系統(tǒng)的布局通過installDecor()
加載完成之后零聚,就會通過mLayoutInflater.inflate(layoutResID, mContentParent)
加載我們自己設(shè)置進(jìn)去的布局。
而inflate的源碼分析請參考: