注意:AppCompatActivity.setContentView()与Activity.setContentView()主要的区别,Activity.setContentView直接将视图添加到Window上,AppCompatActivity.setContentView()借助AppCompatActivity的Delegate代理类,将要显示的视图加入到代理层视图,代理层视图在添加到Window上;
目录
1)在AppCompatActivity定义了setContentView
大概可以了解如下内容:
a.AppCompatActivity中在onCreate中调用setContentView(R.layout.main)是做什么的,如何装载视图;
b.PhoneWindow是什么,与Window有什么关系;
c.DecorView如何被创建的,与Window和其他视图是什么关系;
d.在我们调用requestFeature的时候为什么要在setContentView之前?
1.setContentView()调用流程
Activity extends AppCompatActivity
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);//android.R.id.content
}
1)在AppCompatActivity定义了setContentView
调用添加视图方法;
//设置要显示视图的布局ID或者View
@Override
public void setContentView(@LayoutRes int layoutResID) {
getDelegate().setContentView(layoutResID);
}
@Override
public void setContentView(View view) {
getDelegate().setContentView(view);
}
@Override
public void setContentView(View view, ViewGroup.LayoutParams params) {
getDelegate().setContentView(view, params);
}
实现了三个重载的setContentView方法,getDelegate()方法负责创建Activity的代理类实例,然后调用setContentView方法添加显示的视图,Activity通过代理模式添加要显示的视图;
2)getDelegate()
负责创建Activity代理AppCompatDelegate类实例;
AppCompatDelegate
//创建Activity代理类实例
public static AppCompatDelegate create(Activity activity, AppCompatCallback callback) {
return create(activity, activity.getWindow(), callback);
}
public static AppCompatDelegate create(Dialog dialog, AppCompatCallback callback) {
return create(dialog.getContext(), dialog.getWindow(), callback);
}
private static AppCompatDelegate create(Context context, Window window,
AppCompatCallback callback) {
if (Build.VERSION.SDK_INT >= 24) {
return new AppCompatDelegateImplN(context, window, callback);
} else if (Build.VERSION.SDK_INT >= 23) {
return new AppCompatDelegateImplV23(context, window, callback);
} else {
return new AppCompatDelegateImplV14(context, window, callback);
}
}
通过代码可以发现根据Android版本不同会实现不同的具体代理类;
代理类的继承关系如下:
具体在代理类AppCompatDelegateImplV9中实现setContentView()方法
@Override
public void setContentView(View v) {
ensureSubDecor();
ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
contentParent.removeAllViews();
contentParent.addView(v);
mOriginalWindowCallback.onContentChanged();
}
@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();
}
@Override
public void setContentView(View v, ViewGroup.LayoutParams lp) {
ensureSubDecor();
ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);
contentParent.removeAllViews();
contentParent.addView(v, lp);
mOriginalWindowCallback.onContentChanged();
}
ensureSubDecor();//负责创建mSubDecor视图(ViewGroup) ,同时将视图添加到Window窗口上(mWindow.setContentView(subDecor);)
ViewGroup contentParent = (ViewGroup) mSubDecor.findViewById(android.R.id.content);//获取mSubDecor下的视图做为父视图
contentParent.removeAllViews();//移除父视图下的所有自视图
contentParent.addView(v);//将Activity下setContentView设置的视图添加到父视图上
mOriginalWindowCallback.onContentChanged();
setContentView调用流程如下:
3)ensureSubDecor();
检测mSubDecor视图是否已经创建,否则负责创建mSubDecor视图(ViewGroup) ,同时将视图添加到Window窗口上;
rivate 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;
// Invalidate if the panel menu hasn't been created before this.
// Panel menu invalidation is deferred avoiding application onCreateOptionsMenu
// being called in the middle of onCreate or similar.
// A pending invalidation will typically be resolved before the posted message
// would run normally in order to satisfy instance state restoration.
PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
if (!isDestroyed() && (st == null || st.menu == null)) {
invalidatePanelMenu(FEATURE_SUPPORT_ACTION_BAR);
}
}
}
mSubDecorInstalled表示mSubDecor视图是否创建,没创建则调用createSubDecor()方法创建;
4)createSubDecor()
负责创建mSubDecor视图(ViewGroup) ,同时将视图添加到Window窗口上;
private ViewGroup createSubDecor() {
//获取主题数组
TypedArray a = mContext.obtainStyledAttributes(R.styleable.AppCompatTheme);
//AppCompatActivity需要设置Theme.AppCompat主题,否则抛出异常
if (!a.hasValue(R.styleable.AppCompatTheme_windowActionBar)) {
a.recycle();
throw new IllegalStateException(
"You need to use a Theme.AppCompat theme (or descendant) with this activity.");
}
//1.初始化视图显示相关特征
if (a.getBoolean(R.styleable.AppCompatTheme_windowNoTitle, false)) {
//样式没有Title
requestWindowFeature(Window.FEATURE_NO_TITLE);
} else if (a.getBoolean(R.styleable.AppCompatTheme_windowActionBar, false)) { //样式显示ActionBar
// Don't allow an action bar if there is no title.
requestWindowFeature(FEATURE_SUPPORT_ACTION_BAR);
}
if (a.getBoolean(R.styleable.AppCompatTheme_windowActionBarOverlay, false)) {
requestWindowFeature(FEATURE_SUPPORT_ACTION_BAR_OVERLAY);
}
if (a.getBoolean(R.styleable.AppCompatTheme_windowActionModeOverlay, false)) {
requestWindowFeature(FEATURE_ACTION_MODE_OVERLAY);
}
mIsFloating = a.getBoolean(R.styleable.AppCompatTheme_android_windowIsFloating, false);
a.recycle();
//确认Window上是否已经安装DecorView,没有则创建DecorView并添加到Window上
mWindow.getDecorView();
final LayoutInflater inflater = LayoutInflater.from(mContext);
ViewGroup subDecor = null;//依据相关参数设置创建subDecor,并添加到Window上
//2.上面说了主题默认都是NoTitle,所以不会走里面的方法
if (!mWindowNoTitle) {
if (mIsFloating) {
// 如果是弹框Dialog,则加载弹框视图
subDecor = (ViewGroup) inflater.inflate(
R.layout.abc_dialog_title_material, null);
// 浮动窗口没有ActionBar,重设置标志
mHasActionBar = mOverlayActionBar = false;
} else if (mHasActionBar) {//有ActionBar
/**
*这需要一些解释。因为我们不能使用android:theme属性
*pre-L,我们通过使用
*ContextThemeWrapper指向actionBarTheme。
*/
TypedValue outValue = new TypedValue();
mContext.getTheme().resolveAttribute(R.attr.actionBarTheme, outValue, true);
Context themedContext;
if (outValue.resourceId != 0) {
themedContext = new ContextThemeWrapper(mContext, outValue.resourceId);
} else {
themedContext = mContext;
}
// 通过themedContext加载视图,并设置为内容视图
subDecor = (ViewGroup) LayoutInflater.from(themedContext)
.inflate(R.layout.abc_screen_toolbar, null);
mDecorContentParent &