setContentView()源码的理解
通俗的解释
使用场景
在Activity中的onCreate()方法中,我们会使用setContentView()给Activity加载布局。但实际情况却不是我们通常使用时理解的那样。正如setContentView()方法名那样,是将layout设置给content,而不是设置给Activity。
先放结论
Activity的布局是有特定的模板,我们通常只是给Content区域填充View
以screen_custom_title.xml模板为例(查看源码请按这里),会得到下图
简单梳理一下执行流程:
以findViewById()为例,来进一步理解
在Activity中,我们使用findViewById()都很熟练,感觉没什么特别的。但是在Framework中,想要找到特定的控件对象,会使用
view.findViewById(id);
来获取相应的控件。实际上,findViewById()是View当中的方法,在Activity中,findViewById()实际上是调用DecorView.findViewById(),只是不给使用者展示。也就是说,DecorView是外层的布局,也从侧面验证了上图。
RTFSC解释
为了把握整体思路,这里并没有详细地说明每一行代码
Activity中的相关代码
public void setContentView(@LayoutRes int layoutResID) {
//会调用PhoneWindow中的代码
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
PhoneWindow中setContentView()相关代码
public void setContentView(int layoutResID) {
//mContentParent,也就是Content(上图浅紫色,与Title平级的部分)
if (mContentParent == null) {
//DecorView会在这里初始化,ViewGroup也在该方法中添加到DecorView中了,同时找打了mContentParent对象
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 {
//将我们的Layout填充到mContentParent中
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
}
installDecor()中相关的代码,有省略代码
private void installDecor() {
if (mDecor == null) {
//初始化DecorView
mDecor = generateDecor();
//一些相关的配置
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
}
if (mContentParent == null) {
//将ViewGroup添加到DecorView中,并将Content找到,返回
mContentParent = generateLayout(mDecor);
mDecor.makeOptionalFitsSystemWindows();
//自带装饰的Content
//DecorContentParent名字起的也很生动,带装饰的
final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById(
R.id.decor_content_parent);
if (decorContentParent != null) {
//对decorContentParent的操作
//设置logo,title,icon,options...
} else {
//对Title的一些操作
//...
}
if (mDecor.getBackground() == null && mBackgroundFallbackResource != 0) {
mDecor.setBackgroundFallback(mBackgroundFallbackResource);
}
// Only inflate or create a new TransitionManager if the caller hasn't
// already set a custom one.
if (hasFeature(FEATURE_ACTIVITY_TRANSITIONS)) {
//一些过渡的操作
//...
}
}
}
generateLayout中相关的代码,有省略
protected ViewGroup generateLayout(DecorView decor) {
//获取相关属性
TypedArray a = getWindowStyle();
if (false) {
//估计是调试用的代码
//...
}
//各种属性的设置
//...
//ViewGroup的Layout用的id
int layoutResource;
int features = getLocalFeatures();
//根据设置来确定layoutResource是哪个layout
//...
//做个开始标记
mDecor.startChanging();
//填充ViewGroup
View in = mLayoutInflater.inflate(layoutResource, null);
//将ViewGroup添加到DecorView中
decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
mContentRoot = (ViewGroup) in;
//获取Content对象
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
//一些操作
//...
//做个结束标记
mDecor.finishChanging();
//返回ViewGroup
return contentParent;
}
结语
setContentView()相关的代码还是很多的,需要抓住重点,这样才不会影响整体的思维。
如果看完了还是感觉云里雾里,那就先把图片记住,剩下的以后再说。
转载请标明出处:http://blog.youkuaiyun.com/qq_26411333/article/details/51541129