我們都知道在Activity中通過setContentView(layoutId)
來載入佈局檔案,使我們的佈局檔案能夠顯示在手機上,那麼系統是如何將我們的佈局檔案載入到介面上的呢?
setContentView
// Activity.java
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
複製程式碼
通過setContentView
的原始碼可以發現其實是呼叫了Window的setContentView方法,而Window是一個抽象類,PhoneWindow是Window的實現類。
// 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()
建立出DecorView。
之後再通過mLayoutInflater.inflate(layoutResID, mContentParent)
將我們的佈局載入進記憶體。
DecorView
private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {
// 建立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個作用:
-
建立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(); // 各種判斷載入系統佈局 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
的作用就是:-
通過條件獲取到系統需要載入的佈局檔案,這裡我們以最簡單的
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); 複製程式碼
至此係統的佈局就載入好了,大概的結構如下:
-
inflate載入我們的佈局
當系統的佈局通過installDecor()
載入完成之後,就會通過mLayoutInflater.inflate(layoutResID, mContentParent)
載入我們自己設定進去的佈局。
而inflate的原始碼分析請參考: