Android-setContentView做的一些事
從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結構大致如下圖(圖片是借用網路上一個哥們的)