关于View的绘制流程,前前后后也看了很多相关的文章,零零散散的学习了很多东西,但是一直没有时间进行细致的梳理。借着这次假期时间,从头到尾的梳理一遍。
前言
想到UI布局之类的我们最熟悉的应该就是Activity的布局加载函数setContentView()
,也就是在生命周期onCreate()
中AS默认生成的:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
那就从这里开始,一点点的从源码捋一下。
源码
首先进入Activity
:
<Activity.java>
public void setContentView(@LayoutRes int layoutResID) {
//通过mWindow的setContentView
getWindow().setContentView(layoutResID);
//初始化标题栏
initWindowDecorActionBar();
}
这里完成了两个工作,分别设置我们的自定义布局和初始化ActionBar
,这里我们先看自定义布局,继续跟进去看源码,进入Window
的唯一实现类PhoneWindow
:
<PhoneWindow.java>
public void setContentView(int layoutResID) {
//1.初始化DecorView
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
//2.加载自定义布局
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;
}
首先,完成了第一个主要任务installDecor()
,这里只保留了关键函数:
<PhoneWindow.java>
private void installDecor() {
......
mDecor = generateDector(-1);
......
mContentParent = generateLayout(mDecor);
......
}
在这里分别初始化了mDecorView
和mContentParent
,其中generateDector()
中调用的DecorView
的构造函数创建了一个DecorView
对象,然后进入了generateLayout()
<PhoneWindow.java>
protected ViewGroup generateLayout(DecorView decor) {
......
根据主题和特性获取不同的layoutResource
......
//根据layoutResource创建一个ViewGroup,加载到mDecorView中
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
//通过Id获取R.,android.content,此ViewGroup存在于layoutResource布局中
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
......
//返回mContentParent的引用
return contentParent;
}
至此,第一步初始化DecorView
的工作结束。后面就是加载布局的工作,将自定义布局加载到DecorView
中。
<PhoneWindow.java>
mLayoutInflater.inflate(layoutResID, mContentParent);
流程
好了,现在我们梳理一下在setContentView()中都做了什么,上图:
setContentView.png
-
setContentView(): 这里直接调用了
phoneWindow.setContentView()
,并且初始化了ActionBar
; -
installDecor(): 初始化窗口的基础布局,主要工作在
generateDecor()
和gengrateLayout()
中进行; -
generateDecor(): 创建
DecorView
存储在mDecorView
; -
gengrateLayout(): 根据当前主题环境选择合适的基础布局,
onResourcesLoaded()
创建mContentParent
,并return
该布局中的android.R.id.content
; -
onResourcesLoaded():
inflater.inflate(layoutResource, null)
将选择出来的合适的布局创建出来,并addView()
到DecorView
中;
大致情况如下图:
示意图.png
总结:
View的绘制是通过嵌套的方式从DecorView开始向下一层一层的迭代绘制,setContentView(),的作用就是在Activity的生命周期回调中,将自定义layout布局嵌入到DecorView中;
网友评论