最近在熟悉Android的源码,今天看一下Activity加载SetContentView(int resId)整个流程。
1.Activity.setContentView(int layoutResID)
/**
* Set the activity content from a layout resource. The resource will be
* inflated, adding all top-level views to the activity.
* @param layoutResID Resource ID to be inflated.
* @see #setContentView(android.view.View)
* @see #setContentView(android.view.View, android.view.ViewGroup.LayoutParams)
*/
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
首先调用getWindow()获取Window对象(mWindow),然后调取Window的setContentView(int layoutResID)方法,我们知道Window是一个抽象类,它的具体实现类是PhoneWindow,所以是调的phoneWindow的setContentView(int layoutResID)方法。
mWindow = new PhoneWindow(this, window, activityConfigCallback);
2.PhoneWindow.setContentView(int layoutResID)
public void setContentView(int layoutResID) {
// 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.
//mContentParent ==null的时候调用installDecor方法
if (mContentParent == null) {
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 {
//将我们的布局文件layoutResID加入到mContentParent,可以看出mContentParent包裹了我们的整个布局文件。
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}
mContentParent这个ViewGroup是在什么地方初始化的呢?当我们判断mContentParent == null的时候调用方法 installDecor(),所以相信mContentParent是在installDecor()里面初始化的,不信我们进去看看。
3.PhoneWindow.installDecor()
// This is the top-level view of the window, containing the window decor.最顶层的View层,包含整个窗口
private DecorView mDecor;
private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {
//mDecor如果为null,则生成
mDecor = generateDecor(-1);
。。。。。//此处省略
} else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
//此处生成mContentParent
mContentParent = generateLayout(mDecor)
。。。。。//此处省略
}
}
//此处New了一个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.
。。。。。//此处省略
return new DecorView(context, featureId, this, getAttributes());
}
//生成mContentParent
protected ViewGroup generateLayout(DecorView decor) {
int layoutResource;
// 都是一些判断,根据不同的条件加载不同的 系统资源文件
int features = getLocalFeatures();
// System.out.println("Features: 0x" + Integer.toHexString(features));
if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
layoutResource = R.layout.screen_swipe_dismiss;
setCloseOnSwipeEnabled(true);
} else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
if (mIsFloating) {
TypedValue res = new TypedValue();
getContext().getTheme().resolveAttribute(
R.attr.dialogTitleIconsDecorLayout, res, true);
layoutResource = res.resourceId;
} else {
layoutResource = R.layout.screen_title_icons;
}
// XXX Remove this once action bar supports these features.
removeFeature(FEATURE_ACTION_BAR);
// System.out.println("Title Icons!");
}
。。。。。//此处省略
mDecor.startChanging();
// 把布局解析加载到 DecorView 而加载的布局是一个系统提供的布局,不同版本不一样
// 方法内部都是调用addView方法
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
// ID_ANDROID_CONTENT 是 android.R.id.content,这个View是从DecorView里面去找的,
// 也就是从系统的layoutResource里面找一个id是android.R.id.content的一个FrameLayout
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
// 返回
return contentParent;
}
总结:在Activity里面设置setContentView(),显示布局主要是通过PhoneWindow实例化一个DecorView,然后通过generateLayout()方法根据条件加载系统的资源文件,然后在资源文件里面找到一个d是android.R.id.content的一个FrameLayout,然后将我们的布局文件解析到这个FrameLayout里面。
image.png本文主要参考红橙Darren的博客及视频讲解。
网友评论