View
绘制可以说是Android
开发的必备技能,但是关于View
绘制的的知识点也有些繁杂。
如果我们从头开始阅读源码,往往千头万绪,抓不住要领。
目前当我们写页面时,布局都是写在XML
里的,我们可以思考下:布局从XML
到显示到屏幕上,都发生了什么,可以分为哪几个部分?
我们将整个显示流程分解为以下几个部分
- 代码是怎么从
XML
转换成View
的? View
是怎么添加到页面上的?- 在内存中
View
到底是怎么绘制的? View
绘制完成后是怎么显示到屏幕上的?
本文目录如下所示:
1. XML
是怎么转换成View
的?
我们都知道,在android
中写布局一般是通过XML
,然后通过setContentView
方法配置到页面中
看来XML
转换成View
就是在这个setContentView
中了
1.1 setContentView
中做了什么
public void setContentView(int resId) {
ensureSubDecor();
ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
contentParent.removeAllViews();
LayoutInflater.from(mContext).inflate(resId, contentParent);
mAppCompatWindowCallback.getWrapped().onContentChanged();
}
可以看到resId
传给了我们熟悉的LayoutInflater
,看来xml
转化成View
就是在LayoutInflater
方法中实现的了
1.2 LayoutInflater
中做了什么?
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
final Resources res = getContext().getResources();
//预编译直接返回view,目前还未启用
View view = tryInflatePrecompiled(resource, res, root, attachToRoot);
if (view != null) {
return view;
}
XmlResourceParser parser = res.getLayout(resource);
try {
//真正将`XML`转化为`View`
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}
代码也比较简单,我们一起来分析下
- 首先我们需要明确,将
XML
转化为View
牵涉到一些耗时操作,比如XML
解析是一个io
操作,将XML
转化为View
涉及到反射,这也是耗时的 - 我们可以看到在解析前有个
tryInflatePrecompiled
方法,这个方法就是希望可以在编译阶段直接预编译XML
,在运行时直接返回构建好的View
,看起来Google
希望通过这种方式解决XML
的性能问题。不过这个功能目前还没有启用,因此此方法直接返回null
,目前生效的还是下面的方法 - 真正将
XML
解析为View
的还是在inflate
方法中,将标签名转化为View
的名称,XML
中的各种属性转化为AttributeSet
对象,然后通过反射生成View
对象
由于篇幅原因,这里就不再粘贴inflate
方法的源码了,里面主要需要注意下setFactory
与setFactory2
方法
在真正进行反射前,会先调用这两个方法尝试创建一下View
,而且系统开放了API
,我们可以自定义解析XML
方式
这就给了我们一些面向切面编程的空间,可以利用这两个API
实现换肤,替换字体,替换 View
,提升View
构建速度等操作。
1.3 小结
XML
转化为View
转化为主要是通过LayoutInflator
来完成的,将标签名转化为View
的名称,XML
中的各种属性转化为AttributeSet
对象,然后通过反射生成View
对象
这个过程中存在一些耗时操作,比如解析XML
的IO
操作,通过反射生成View
等,我们可以通过多种方式优化这个过程,比如将反向的耗时转移到编译期。