为什么((ViewGroup)getWindow.getDecorView().findViewById(android.R.id.content)).getChildAt(0),能够获取我们通过setContentView设置的layout布局对象?
这主要是因为layout布局就放在android.R.id.content对应的控件中。大概流图如下:
执行顺序为1, 2, 4, 5, 3,接下来通过代码片段来说明。
本文基于API19的源代码讲解。
我们会在Activity中调用setContentView(R.layout.main)来设置布局,以下为主要代码片段
1.1 Activity.setContentView
/**
* Set the activity content to an explicit view.
* 将显视的view设置到activity的content中
* This view is placed directly into the activity's view hierarchy.
* view被直接放置到activity的view层次结构中
* It can itself be a complex view hierarchy.
* activity本身就是一个复杂的view层次结构
*/
public void setContentView(int layoutResID) {
//调用了Window的setContentView,实际调用的是子类PhoneWindow的方法
getWindow().setContentView(layoutResID);
initActionBar();
}
Activity提供的方法注释
1.2 Window.setContentView
/**
* Convenience for
* {@link #setContentView(View, android.view.ViewGroup.LayoutParams)}
* set the screen content to an explicit view. This view is placed
* directly into the screen's view hierarchy. It can itself be a complex
* view hierarhcy.
*/
public abstract void setContentView(int layoutResID);
1.3 PhoneWindow.setContentView
@Override
public void setContentView(int layoutResID) {
if (mContentParent == null) {
installDecor();
} else {
mContentParent.removeAllViews();
}
mLayoutInflater.inflate(layoutResID, mContentParent);
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
}
1.3.1 PhoneWindow.setContentView->installDecor
private void installDecor() {
if (mDecor == null) {
mDecor = generateDecor();
...
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
...
}
}
1.3.1.1 PhoneWindow.setContentView->installDecor->generateDecor
实例化DecorView对象
protected DecorView generateDecor() {
return new DecorView(getContext(), -1);
}
1.3.1.2 PhoneWindow.setContentView->installDecor->generateLayout
向DecorView中添加系统预设的layout,并返回R.id.content对应FrameLayout控件
protected ViewGroup generateLayout(DecorView decor) {
...
if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
//系统提供的布局文件
layoutResource = com.android.internal.R.layout.screen_action_bar;
} else {
//系统提供的布局文件
layoutResource = com.android.internal.R.layout.screen_title;
}
...
mDecor.startChanging();
//实例化布局screen_action_bar(debug时走的这条路),并将其添加到DecorView中
View in = mLayoutInflater.inflate(layoutResource, null);
//我们在activity中设置的布局文件,最后就会添加到这个布局中
decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
//找到R.id.content控件FrameLayout
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
if (contentParent == null) {
throw new RuntimeException("Window couldn't find content container view");
}
...
return contentParent;
}
1.3.2 PhoneWindow.setContentView->LayoutInflater.inflate
/**
* Inflate a new view hierarchy from the specified xml resource. Throws
* {@link InflateException} if there is an error.
* 将xml布局文件转化为一个view层次结构
* @param resource ID for an XML layout resource to load (e.g.,
* <code>R.layout.main_page</code>)
* 通过setContentView设置的布局资源
* @param root Optional view to be the parent of the generated hierarchy.
* root为DecorView中,id是R.id.content的FrameLayout控件
* @return The root View of the inflated hierarchy. If root was supplied,
* this is the root View; otherwise it is the root of the inflated
* XML file.
* 提供root,则转换出来的view有root相关层次结构;没有提供则转换出来的只有xml中的view层次结构
*/
public View inflate(int resource, ViewGroup root) {
return inflate(resource, root, root != null);
}
public View inflate(int resource, ViewGroup root, boolean attachToRoot) {
if (DEBUG) System.out.println("INFLATING from resource: " + resource);
XmlResourceParser parser = getContext().getResources().getLayout(resource);
try {
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}
public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {
...
View result = root;
...
// Inflate all children under temp
rInflate(parser, temp, attrs, true);
if (DEBUG) {
System.out.println("-----> done inflating children");
}
// We are supposed to attach all the views we found (int temp)
// to root. Do that now.
if (root != null && attachToRoot) {
root.addView(temp, params);
}
...
//以上步骤将resource中的view全部添加到root中
return result;
}