View绘制体系(一)——从setContentView聊起
前言
对于Android开发者来说,View的绘制是非常基础且重要的部分,而Activity绘制View的流程,我们都是从setContentView开始去设置我们自定义的布局的,所以我准备从setContentView为起点来聊下View的绘制流程。
setContentView
在Activity中,我们经常会调用到setContentView这个方法来设置对应的布局文件,在这里我想从源码的角度去分析下setContentView内部是如何实现的。需要注意的是,setContentView并没有绘制View,只是创建了View。
我们先来看下Activity
类中的setContentView
方法:
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
public void setContentView(View view) {
getWindow().setContentView(view);
initWindowDecorActionBar();
}
public void setContentView(View view, ViewGroup.LayoutParams params) {
getWindow().setContentView(view, params);
initWindowDecorActionBar();
}
可以看到setContentView的三个重载其内部都是调用的getWindow().setContentView
对应参数的三个重载方法,然后调用initWindowDecorActionBar()
来初始化标题栏,那么我们就看看getWindow()
方法。
public Window getWindow() {
return mWindow;
}
根据上面代码,可以看到mWindow
是Activity
中的一个变量,保存与Activity
对应的Window
对象,Window
是个抽象类,所以我们要找到该对象初始化的地方,在Activity
中的attach
方法里面:
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, ActivityConfigCallback activityConfigCallback) {
attachBaseContext(context);
//...
mWindow = new PhoneWindow(this, window, activityConfigCallback);
//...
}
由此可以看到PhoneWindow
是Window
的实现子类,而Activity
的setContentView
实质是调用了PhoneWindow
的setContentView
方法,那么就来看下这个类,我们先来看下PhoneWindow.setContentView(int layoutResID)
这个方法:
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)) {
//FEATURE_CONTENT_TRANSITIONS表示开启了动画Transition效果
//移除mContentParent中所有的子View
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
//开启Transiton后做相应的处理,不做具体分析
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
//一般情况来到这里,加载布局
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb