引言
今天来研究一下Android中setContentView()方法的具体实现。
找入口
下面的代码是每一个Androider最熟悉的了吧
setContentView(R.layout.main_activity)
没错就从这里作为入口,看下去,向上追溯到Activity.java中是这样的
public void setContentView(int layoutResID) {
getWindow().setContentView(layoutResID);
initActionBar();
}
可以看出这里调用了getWindow.setContentView(int id)这个方法,点进去看就可以看到这是Window类的一个抽象方法。
public abstract void setContentView(int layoutResID);
然后网上查了一下PhoneWindow类是Window的实现类(至于怎么关联起来的,我暂时还没理解,有知道的网友可以说一下),看看PhoneWindow的源码。
源码位置:
Android\sources\android-16\com\android\internal\policy\impl\PhoneWindow.java
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();
}
}
判断mContentParent( 这是mContentParent的定义private ViewGroup mContentParent;)是否为null,如果是执行installDecor();如果不是则移除mContentParent的所有子View.总而言之,这里就是要一个干净的mContentParent.
之后的代码就是用LayoutInflater将xml布局装载到mContentParent.然后通过回调函数告诉Activity.
installDecor()
先看看这个函数的源码
private void installDecor() {
if (mDecor == null) {
mDecor = generateDecor();//mDecor是DecorView的对象,该类继承了FrameLayout
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);//对mDecor进行一下包装,同时生成主布局
// Set up decor part of UI to ignore fitsSystemWindows if appropriate.
mDecor.makeOptionalFitsSystemWindows();
mTitleView = (TextView)findViewById(com.android.internal.R.id.title);//titleView相关
if (mTitleView != null) {
//mTitleView相关,省略
} else {
mActionBar = (ActionBarView) findViewById(com.android.internal.R.id.action_bar);
//省略一大段和ActionBar相关的代码
}
}
}
从上面的代码可以看出,系统生成了一个mDecor,设置相关的参数,生成一个ViewGroup。
generateLayout()
这个是生成ViewGroup的方法
protected ViewGroup generateLayout(DecorView decor) {
// Apply data from current theme.
TypedArray a = getWindowStyle();//获取到参数数组
if (false) {
System.out.println("From style:");
String s = "Attrs:";
for (int i = 0; i < com.android.internal.R.styleable.Window.length; i++) {
s = s + " " + Integer.toHexString(com.android.internal.R.styleable.Window[i]) + "="
+ a.getString(i);
}
System.out.println(s);
}
mIsFloating = a.getBoolean(com.android.internal.R.styleable.Window_windowIsFloating, false);
int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR)
& (~getForcedWindowFlags());
if (mIsFloating) {
setLayout(WRAP_CONTENT, WRAP_CONTENT);
setFlags(0, flagsToUpdate);
} else {
setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate);
}
//……省略,基本上是配置ViewGroup的Style等。
//主要看看下面这段
mDecor.startChanging();
View in = mLayoutInflater.inflate(layoutResource, null);
decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
//这个是xml文件中main布局
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
if (contentParent == null) {
throw new RuntimeException("Window couldn't find content container view");
}
if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {
ProgressBar progress = getCircularProgressBar(false);
if (progress != null) {
progress.setIndeterminate(true);
}
}
// Remaining setup -- of background and title -- that only applies
// to top-level windows.
if (getContainer() == null) {
Drawable drawable = mBackgroundDrawable;
if (mBackgroundResource != 0) {
drawable = getContext().getResources().getDrawable(mBackgroundResource);
}
mDecor.setWindowBackground(drawable);
drawable = null;
if (mFrameResource != 0) {
drawable = getContext().getResources().getDrawable(mFrameResource);
}
mDecor.setWindowFrame(drawable);
// System.out.println("Text=" + Integer.toHexString(mTextColor) +
// " Sel=" + Integer.toHexString(mTextSelectedColor) +
// " Title=" + Integer.toHexString(mTitleColor));
if (mTitleColor == 0) {
mTitleColor = mTextColor;
}
if (mTitle != null) {
setTitle(mTitle);
}
setTitleColor(mTitleColor);
}
mDecor.finishChanging();
return contentParent;
}
可以看出这个方法里对mDecor做了一些配置工作,然后将主布局生成ViewGroup返回。
总结一下
Activity.setContentView—>(PhoneWindow)Window.setContentView()—>生成一个DecorView(也就是一个FrameLayout)和主布局ViewGroup(根据传进去的XML文件的ID)。
这也就是为什么我们用hierachyviewer工具看到根View是一个FrameLayout的缘故。
另外说一句关于Window和PhoneWindow的关系,参考了这篇文章。
补充说明Window、PhoneWindow与DecorView