從MainActivity的setContentView進入根據源碼追蹤找到AppCompatDelegate的實現類AppCompatDelegateImplV9,從AppCompatDelegateImplV9#setContentView中可以看出其源碼
@Override
public void setContentView(int resId) {
ensureSubDecor();
ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
contentParent.removeAllViews();
LayoutInflater.from(mContext).inflate(resId, contentParent);
mOriginalWindowCallback.onContentChanged();
}
將我們MainActivity#setContentView的resId通過LayoutInflater.from(mContext).inflate(resId, contentParent);實例化,添加到界面;
其中mSubDecor則是調用我們AppCompatDelegateImplV9#ensureSubDecor()創建出來的
private void ensureSubDecor() {
if (!mSubDecorInstalled) {
mSubDecor = createSubDecor();
// If a title was set before we installed the decor, propagate it now
CharSequence title = getTitle();
if (!TextUtils.isEmpty(title)) {
onTitleChanged(title);
}
applyFixedSizeWindow();
onSubDecorInstalled(mSubDecor);
mSubDecorInstalled = true;
PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
if (!isDestroyed() && (st == null || st.menu == null)) {
invalidatePanelMenu(FEATURE_SUPPORT_ACTION_BAR);
}
}
}
mSubDecorInstalled先判斷了是否有這個佈局
其中mSubDecor在mSubDecor = createSubDecor()時候創建實例化
private ViewGroup createSubDecor() {
// 省略部分代碼,都是一些屬性的判斷
// Now let's make sure that the Window has installed its decor by retrieving it
mWindow.getDecorView();
final LayoutInflater inflater = LayoutInflater.from(mContext);
ViewGroup subDecor = null;
// 省略部分代碼,對各種情況進行判斷,對subDecor進行實例化,如果到這步還是爲空就拋出一個異常
if (subDecor == null) {
throw new IllegalArgumentException(
"AppCompat does not support the current theme features: { "
+ "windowActionBar: " + mHasActionBar
+ ", windowActionBarOverlay: "+ mOverlayActionBar
+ ", android:windowIsFloating: " + mIsFloating
+ ", windowActionModeOverlay: " + mOverlayActionMode
+ ", windowNoTitle: " + mWindowNoTitle
+ " }");
}
// 省略部分代碼,都是通過 subDecor 實例化的佈局
// Now set the Window's content view with the decor
mWindow.setContentView(subDecor);
contentView.setAttachListener(new ContentFrameLayout.OnAttachListener() {
@Override
public void onAttachedFromWindow() {}
@Override
public void onDetachedFromWindow() {
dismissPopups();
}
});
return subDecor;
}
AppCompatDelegateImplV9#createSubDecor()中先判斷了一系列的屬性,然後就
調用mWindow.getDecorView();判斷DecorView是否已經實例化,最後調用了mWindow.setContentView(subDecor); 將subDecor設置進去;
Window是一個抽象類,根據說明可以知道PhoneWindow實例化了繼承自這個類,所以可以查看
PhoneWindow#getDecorView(),在這個方法裏面做了調用PhoneWindow#installDecor()這一件事
private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {
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) {
mContentParent = generateLayout(mDecor);
// Set up decor part of UI to ignore fitsSystemWindows if appropriate.
mDecor.makeOptionalFitsSystemWindows();
final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById(
R.id.decor_content_parent);
// 省略部分代碼,都是一些屬性的判斷和控件實例化
}
}
沿着裏面往下走可以知道DecorView是由mDecor = generateDecor(-1)創建生成的;
在PhoneWindow#generateDecor()中通過return new DecorView(context, featureId, this, getAttributes());創建一個DecorView
protected DecorView generateDecor(int featureId) {
// System process doesn't have application context and in that case we need to directly use
// the context we have. Otherwise we want the application context, so we don't cling to the
// activity.
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();
}
// 最後創建一個DecorView返回
return new DecorView(context, featureId, this, getAttributes());
}
回到AppCompatDelegateImplV9#createSubDecor()中,接着mWindow.getDecorView();往下走,看到mWindow.setContentView(subDecor); 這裏也可以繼續跳到PhoneWindow處搜索PhoneWindow#setContentView()
@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();
}
mContentParentExplicitlySet = true;
}
在PhoneWindow#setContentView()中,一開始的mSubDecor通過mContentParent.addView(view, params);添加到mContentParent中
mContentParent是在PhoneWindow#installDecor()裏面在DecorView之後創建出來的並且通過PhoneWindow#generateLayout(DecorView decor)方法創建出來並添加至DecorView裏面
protected ViewGroup generateLayout(DecorView decor) {
// 省略部分代碼,都是判斷一些主題和屬性
// 開始將控件添加入DecorView
mDecor.startChanging();
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
// 實例化mContentParent,其中findViewById(ID_ANDROID_CONTENT)調用的是Window這個類裏面的findViewById,
// 在裏面通過getDecorView().findViewById(id)進行賦值,getDecorView()就是PhoneWindow的DecorView
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
if (contentParent == null) {
throw new RuntimeException("Window couldn't find content container view");
}
if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {
ProgressBar progress = getCircularProgressBar(false);
if (progress != null) {
progress.setIndeterminate(true);
}
}
if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
registerSwipeCallbacks();
}
// Remaining setup -- of background and title -- that only applies
// to top-level windows.
if (getContainer() == null) {
final Drawable background;
if (mBackgroundResource != 0) {
background = getContext().getDrawable(mBackgroundResource);
} else {
background = mBackgroundDrawable;
}
mDecor.setWindowBackground(background);
final Drawable frame;
if (mFrameResource != 0) {
frame = getContext().getDrawable(mFrameResource);
} else {
frame = null;
}
mDecor.setWindowFrame(frame);
mDecor.setElevation(mElevation);
mDecor.setClipToOutline(mClipToOutline);
if (mTitle != null) {
setTitle(mTitle);
}
if (mTitleColor == 0) {
mTitleColor = mTextColor;
}
setTitleColor(mTitleColor);
}
mDecor.finishChanging();
return contentParent;
}
在PhoneWindow#generateLayout(DecorView decor)裏面通過
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
實例化mContentParent,其中findViewById(ID_ANDROID_CONTENT)調用的是Window這個類裏面的findViewById,在Window裏面Window#findViewById()通過getDecorView().findViewById(id)進行實例化,getDecorView()就是PhoneWindow的DecorView
整個的setContentView結構大致如下圖(圖片是借用網絡上一個哥們的)