我们在继承Activity、实现onCreate方法的时候,写下的第一行代码可能就是去实现setContentView这个方法了。那么setContentView具体是怎么实现的呢,今天我们来分析一下setContentView的实现过程。
setContentView的具体流程
/**
* 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(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
通过上面的方法我们可以看到三个方法,分别是getWindow()、getWindow().setContentView(layoutResID)、还有initWindowDecorActionBar()。我们一个一个看,在getWindow()方法中
/**
* Retrieve the current {@link android.view.Window} for the activity.
* This can be used to directly access parts of the Window API that
* are not available through Activity/Screen.
*
* @return Window The current window, or null if the activity is not
* visual.
*/
public Window getWindow() {
return mWindow;
}
getWindow()方法中返回了一个Window对象,但是在这个方法中没有体现Window对象实在什么时候创建的。我们通过搜索Activity中的mWindow对象可以看到在Activity的attach方法中实例化了mWindow对象。
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, String referrer, IVoiceInteractor voiceInteractor,
Window window) {
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);
mWindow = new PhoneWindow(this, window);
mWindow.setWindowControllerCallback(this);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
mWindow.setSoftInputMode(info.softInputMode);
}
if (info.uiOptions != 0) {
mWindow.setUiOptions(info.uiOptions);
}
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
if (mParent != null) {
mWindow.setContainer(mParent.getWindow());
}
拿到mWindow对象之后调用了mWindow.setContentView的方法,点进去我们看到的是Window这个抽象类,并没有看到具体的实现内容,在attach方法中我们看到的是mWindow = new PhoneWindow(this, window),其实PhoneWindow是Window类的实现类。我们到PhoneWindow中去查看setContentView方法。
@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);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}
首先mContentParent是一个ViewGroup,我们先对mContentParent进行判空处理,如果为空那么我们需要获取DecorView,如果mContentParent不为空,那么移除当前ViewGroup的所有子View。然后添加我们指定的XML文件,通过Callback回调来更新视图。上面还有三处可能大家不是很清楚。第一个是mLayoutInflater从哪里来的,第二是Callback回调到哪里去了,第三个是 installDecor()方法做了什么。
public PhoneWindow(Context context) {
super(context);
mLayoutInflater = LayoutInflater.from(context);
}
mLayoutInflater再PhoneWindow实例化的时候被赋值,调用了 LayoutInflater.from()方法。
public static LayoutInflater from(Context context) {
LayoutInflater LayoutInflater =
(LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (LayoutInflater == null) {
throw new AssertionError("LayoutInflater not found.");
}
return LayoutInflater;
}
getCallback()方法对应的setCallback()方法是在Activity的attach方法中设置的。看到这里可以看出回调的位置为Activity。
installDecor()方法做了些什么
if (mDecor == null) {
mDecor = generateDecor(-1);
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
} else {
mDecor.setWindow(this);
}
protected DecorView generateDecor(int featureId) {
// System process doesn't have application context and in that case we need to directly use
// the context we have. Otherwise we want the application context, so we don't cling to the
// activity.
Context context;
if (mUseDecorContext) {
Context applicationContext = getContext().getApplicationContext();
if (applicationContext == null) {
context = getContext();
} else {
context = new DecorContext(applicationContext, getContext().getResources());
if (mTheme != -1) {
context.setTheme(mTheme);
}
}
} else {
context = getContext();
}
return new DecorView(context, featureId, this, getAttributes());
}
mDecor是一个DecorView的对象,我们先去判断mDecor是否为空,如果为空,那么回去创建一个mDecor对象,不为空则添加在Window中。
至此,整个setContentView的方法所走的流程就走完了。从这个流程中我们可以看出,跟View有相关的层级都是以Activity为基础的。
Activity>PhoneWindow>DecorView>ViewGroup>View