在写一个Activity的时候,经常会先调用setContentView方法来设置Activity的布局,那么setContentView中究竟做了什么呢?
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
Activity的getWindow()方法在之前,Activity的初始化的时候,就已经了解了,其是在Activity的attach方法中初始化的
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor) {
......
// 初始化PhoneWindow对象
mWindow = new PhoneWindow(this);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
mWindow.setSoftInputMode(info.softInputMode);
}
if (info.uiOptions != 0) {
mWindow.setUiOptions(info.uiOptions);
}
......
}
getWindow()方法中取得的mWindow就是PhoneWindow对象,那么Activity的setContentView首先调用了PhoneWindow中的setContentView方法
@Override
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.
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
......
}
PhoneWindow的installDecor方法
private void installDecor() {
if (mDecor == null) {
// 第一次启动一个Activity的时候,新建mDecor对象
mDecor = generateDecor();
......
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
......
}
}
protected DecorView generateDecor() {
// new一个DecorView对象
return new DecorView(getContext(), -1);
}
private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {
......
}
初始化mDecor,其为DecorView对象,父类为FrameLayout
在看PhoneWindow的generateLayout方法,注意其传入的是DecorView对象
protected ViewGroup generateLayout(DecorView decor) {
......
int layoutResource;
int features = getLocalFeatures();
if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
layoutResource = R.layout.screen_swipe_dismiss;
} 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);
} else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
&& (features & (1 << FEATURE_ACTION_BAR)) == 0) {
// Special case for a window with only a progress bar (and title).
// XXX Need to have a no-title version of embedded windows.
layoutResource = R.layout.screen_progress;
} else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
// Special case for a window with a custom title.
// If the window is floating, we need a dialog layout
if (mIsFloating) {
TypedValue res = new TypedValue();
getContext().getTheme().resolveAttribute(
R.attr.dialogCustomTitleDecorLayout, res, true);
layoutResource = res.resourceId;
} else {
layoutResource = R.layout.screen_custom_title;
}
// XXX Remove this once action bar supports these features.
removeFeature(FEATURE_ACTION_BAR);
} else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
// If no other features and not embedded, only need a title.
// If the window is floating, we need a dialog layout
if (mIsFloating) {
TypedValue res = new TypedValue();
getContext().getTheme().resolveAttribute(
R.attr.dialogTitleDecorLayout, res, true);
layoutResource = res.resourceId;
} else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
layoutResource = a.getResourceId(
R.styleable.Window_windowActionBarFullscreenDecorLayout,
R.layout.screen_action_bar);
} else {
layoutResource = R.layout.screen_title;
}
} else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
layoutResource = R.layout.screen_simple_overlay_action_mode;
} else {
// Embedded, so no decoration is needed.
layoutResource = R.layout.screen_simple;
}
mDecor.startChanging();
View in = mLayoutInflater.inflate(layoutResource, null);
// 将其加入刚刚创建的DecorView对象中
decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
mContentRoot = (ViewGroup) in;
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
......
return contentParent;
}
从这个方法可以看到,generateLayout方法其实是确认Activity的布局文件,然后将其加入刚刚创建的DecorView中,而其返回值为DecorView中的一个id为com.android.internal.R.id.content的View,查看刚刚所有的layout文件,均有一个id为com.android.internal.R.id.content的FrameLayout,那么contentParent对象是做什么用的呢?
返回PhoneWindow的installDecor方法中,可以看到,这个方法中主要是初始化了mDecor和mContentParent对象,而mContentParent是FrameLayout,且是花在mDecor上的
再返回PhoneWindow的setContentView中,我们发现setContentView方法中传递了一个参数(View或者layoutid),那么是在什么地方用的呢?
public void setContentView(int layoutResID) {
......
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 {
// 以mContentParent为父View,传入的layoutResID为layout布局
mLayoutInflater.inflate(layoutResID, mContentParent);
}
......
}
从这儿,我们可以发现,我们传入的layoutResID所对应的布局或者View,就是画在刚刚定义的mContentParent上的
那么得出结论,我们传入的View或者布局,都是画在mContentParent上的,最终是画在DecorView上的,而DecorView为我们提供了一个既定的layout布局