说到android的UI绘制流程,咱们首先介绍一下setContentView这个大家经常用到的方法。当创建一个Activity时,实现了onCreate方法,然后把创建好的布局Layout或者View通过调用setContentView(xxx)就显示到窗口中了。那么接下来就带着下面的疑问,了解View的加载过程。
为什么调用了setContentView后就可以显示出我们的局部页面?
看看源码中setContentView究竟做了什么
private Window mWindow;
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
public Window getWindow() {
return mWindow;
}
从源码中可以看到setContentView中调用了getWindow方法获取了Window对象,看看源码中对Window对象的定义。
/**
* Abstract base class for a top-level window look and behavior policy. An
* instance of this class should be used as the top-level view added to the
* window manager. It provides standard UI policies such as a background, title
* area, default key processing, etc.
*
* <p>The only existing implementation of this abstract class is
* android.view.PhoneWindow, which you should instantiate when needing a
* Window.
*/
public abstract class Window {
public View findViewById(@IdRes int id) {}
public void setBackgroundDrawableResource(@DrawableRes int resId) {}
...
}
从注释中我们看到Window对象是一个顶层窗口的抽象类,里面封装了一些跟窗口UI相关的方法。既然Window是一个抽象类,那么我们就去看一下它的唯一实现类PhoneWindow。
public class PhoneWindow extends Window{
// This is the view in which the window contents are placed. It is either
// mDecor itself, or a child of mDecor where the contents go.
private ViewGroup mContentParent;
// This is the top-level view of the window, containing the window decor.
private DecorView mDecor;
@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();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
mLayoutInflater.inflate(layoutResID, mContentParent);
}
...
}
}
PhoneWindow类中的setContentView方法里面用到了mContentParent,它的类型是ViewGroup,从注释中看到我们的Activity中的View就会放到这个ViewGroup中,换而言之就是Window内容要摆放的一个地方,要么是mDecor自己要么就是mDecor的子类。而这个DecorView就是Window最顶层的View。从代码中看到当mContentParent为空时,调用了installDecor方法,进去看一下。
private void installDecor() {
if (mDecor == null) {
//generateDecor方法中new了一个DecorView
mDecor = generateDecor();
...
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
...
}
}
protected ViewGroup generateLayout(DecorView decor) {
...
// Inflate the window decor.
int layoutResource;
//根据不同的features加载不同的layout布局文件
int features = getLocalFeatures();
if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
...
} else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
...
} else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
&& (features & (1 << FEATURE_ACTION_BAR)) == 0) {
...
} else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
} else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
...
} else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
...
} else {
layoutResource = R.layout.screen_simple;
}
View in = mLayoutInflater.inflate(layoutResource, null);
decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
mContentRoot = (ViewGroup) in;
//代码看到这就可以看出上文提到的mContentParent就是布局
//layoutResource中id为content的FrameLayout,下文中会接着提到。
//public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
return contentParent;
}
DecorView的结构拿最简单的R.layout.screen_simple为例,如下图:
本文原创,转载请注明出处。