参考 http://blog.csdn.net/yanbober/article/details/45970721
参考 上一篇LayoutInflater的学习
我们平时从手机上看到的一个界面,他们其实就是一系列的View对象由树状的形式组合成的View对象树视图。这些View对象被新建出来后,他们会根据自己的绘制规则将自己绘制成视图形式,绘制完毕后由Activity线程将他们整体显示出来。(看完文章后的理解,纯粹为了理解,并没检验正假。。。。反正只有自己看)。下面以及参考链接主要讲的是我们这个View对象树的结构,以及形成原理。
先说结构
看见上面图没?Activity中有一个成员为Window,其实例化对象为PhoneWindow,PhoneWindow为抽象Window类的实现类。
这里先简要说明下这些类的职责:
-
Window是一个抽象类,提供了绘制窗口的一组通用API。
-
PhoneWindow是Window的具体继承实现类。而且该类内部包含了一个DecorView对象,该DectorView对象是所有应用窗口(Activity界面)的根View。
-
DecorView是PhoneWindow的内部类,是FrameLayout的子类,是对FrameLayout进行功能的修饰(所以叫DecorXXX),是所有应用窗口的根View 。
就上面DectorView PhoneWindow Activity我自己的理解是,DectorView就好比是一个相框,PhoneWindow就是管理着相框的相册,而Activity就是拥有相册的主人(我们的相册比较高级,装的是相框),那么相片是什么?相片就是我们主要显示的内容,而将相片装载到相框里我们使用的是setContentView方法。
详细的代码看参考的链接吧
我们分析这种常用的这种类型的setContentView方法
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();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
mLayoutInflater.inflate(layoutResID, mContentParent);
}
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
}
方法一开始就有这段代码
if (mContentParent == null) {
installDecor();}
mContentParent是什么?它是一个容纳我们layoutID对应View或ViewGroup的容器ViewGroup,一开始它当然为空,然后我们执行installDector();
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);
//......
//初始化一堆属性值
}
}
一开始mDecor = generateDecor()仅仅是初始化一个的ViewDecor对象。
然后看方法
mContentParent = 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;
}
首先根据features的值设置layoutResoutce的值,代码中省略这一步,然后View in = mLayoutInflater.inflate(layoutResource, null);生成相应的View
接着mdecor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));将in这个View添加到mdecor.
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);在mdecor中找到ID为ID_ANDROID_CONTENT这个布局设置为contentParent,并将其返回个mContentParent
mContentRoot = (ViewGroup) in;这个就是我们layoutResource对应的View,它也是contentParent的父容器。
回到setContentView方法
mLayoutInflater.inflate(layoutResID, mContentParent);将我们加载的页面添加到mContentParent。
所以为什么我们layoutResID对应布局的layout_....属性得到正常显示,以为我们使用的是mLayoutInflater.inflate(layoutResID, mContentParent);
最后总的层次结构
phoneWindow--ViewDecor--mRootContent--mContentParent--我们自己的布局文件