1,setContentView 分析:
1.1 Activity源码:
/**
* 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(int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
Avtivity中有个Window(抽象类)成员,在attach()中赋值,返回具体的实现类PhoneWindow
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, IVoiceInteractor voiceInteractor) {
attachBaseContext(context);
mFragments.attachActivity(this, mContainer, null);
mWindow = PolicyManager.makeNewWindow(this);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
··························
}
所以说,具体的setContentView实在PhoneWindow中setContentView中进行的。
1.2 PhoneWindow
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();//创建DecorView
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
mLayoutInflater.inflate(layoutResID, mContentParent);//导入xml布局文件
}
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();//加载布局完成时候的回调
}
}
从源码可以看出,主要分3步骤:
1,创建DecorView对象
2,通过LayoutInflater导入xml布局文件
private void installDecor() {
if (mDecor == null) {
mDecor = generateDecor();
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
}
if (mContentParent == null) {
//根据窗口的风格修饰,选择对应的修饰布局文件,并且将id为content的FrameLayout赋值给mContentParent
mContentParent = generateLayout(mDecor);
//......
//初始化一堆属性值
}
}
重在是generateLayout(mDecor)方法
protected ViewGroup generateLayout(DecorView decor) {
// Apply data from current theme.
TypedArray a = getWindowStyle();
//......
//依据主题style设置一堆值进行设置
// Inflate the window decor.
int layoutResource;
int features = getLocalFeatures();
//......
//根据设定好的features值选择不同的窗口修饰布局文件,得到layoutResource值
//把选中的窗口修饰布局文件添加到DecorView对象里,并且指定contentParent值
View in = mLayoutInflater.inflate(layoutResource, null);
decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
mContentRoot = (ViewGroup) in;
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
if (contentParent == null) {
throw new RuntimeException("Window couldn't find content container view");
}
//......
//继续一堆属性设置,完事返回contentParent
return contentParent;
}
可以看出来setContentView整个过程主要是如何把Activity的布局文件或者java的View添加至窗口里,上面的过程可以重点概括为:
1, 创建一个DecorView的对象mDecor,该mDecor对象将作为整个应用窗口的根视图。
2,根据Feature活stylethem创建不同的窗口布局文件,
3,窗口布局文件中id=content的FrameLayout
4,将Activity的xml布局文件放到FrameLayout中
decorView的层次结构如下图:
以上只是把contentView设置完成,接下来解释把它显示出来。
1.3 Acitivity页面显示
Activity调用完Activity的ActivityThread的main之后,调用ActivityThread的performLaunchActivity创建要启动 的Acticity组件,之后调用ActivityThread的handleResumeActivity
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
......
// TODO Push resumeArgs into the activity for consideration
ActivityClientRecord r = performResumeActivity(token, clearHide);
if (r != null) {
......
// If the window hasn't yet been added to the window manager,
// and this guy didn't finish itself or start another activity,
// then go ahead and add the window.
......
// If the window has already been added, but during resume
// we started another activity, then don't yet make the
// window visible.
......
// The window is now visible if it has been added, we are not
// simply finishing, and we are not starting another activity.
if (!r.activity.mFinished && willBeVisible
&& r.activity.mDecor != null && !r.hideForNow) {
......
if (r.activity.mVisibleFromClient) {
r.activity.makeVisible();
}
}
......
} else {
// If an exception was thrown when trying to resume, then
// just end this activity.
......
}
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}
以后在activcity中view ,我们也可以借鉴这个思路:
1,在被添加的layout中 添加占位contentLayout,你的view要添加到什么地方
2,LayoutInflater 的导入布局
3,contentLayout.addView
4,contentLayout.setVisible