从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结构大致如下图(图片是借用网络上一个哥们的)