一、前言
Android的视图是如何绘制的?深入了解一下UI的绘制原理无论对我们APP的性能优化还是对我们的自定义view都有很大的帮助。
二、Activity的视图结构
先看一下activity的视图结构图:
每个activity都有一个Window(实际是phonewindow)
Phonewindow含有一个DecorView,这是我们window的topview
DecorView是继承自Framelayout,换言之其为整个ViewTree的根节点viewGroup
再看一下Phonewindow的类图:
接下来我们来看一下单个Activity的viewTree的结构,我选择了两版sdk来查看
(1)Android4.4系统的activity:
(2)Android6.0系统的activity:
三、ViewTree的绘制
id为“content”的ContentFrameLayout是我们的布局文件加载显示的区域,更确切地说是我们activity的setcontentView()方法设置的视图显示的区域。下面我么就看看ContentFrameLayout中整个viewTree是如何绘制出来的。
这些View应该都具有相同的绘制流程与机制才能显示到屏幕上(可能每个控件的具体绘制逻辑有差异, 但是主流程都是一样的)。每一个View的绘制过程都必须经历三个最主要的过程,也就是measure()、layout()和draw()。
先看一下类图:
那么,整个Android的UI绘制机制是从哪里开始的即入口在哪里呢?答案就是ViewRootImpl类的performTraversals()方法。ViewRootImpl这个类是一个隐藏类,所以如果你是使用Eclipse开发的话可能看不到这个文件(AndroidStudio可以),没关系,根据路径(androidSDK\android-sdk-windows\sources\android-23\android\view\)去找到ViewRootImpl.Java文件,然后用文本阅读工具直接打开就好。
看一下官方对ViewRootImpl的介绍:
/**
* The top of a view hierarchy, implementing the needed protocol between View
* and the WindowManager. This is for the most part an internal implementation
* detail of {@link WindowManagerGlobal}.
*
* {@hide}
*/
上面这段注释啥意思呢?说白了就是ViewRootImpl是一个window中的viewTree的入口,实现了window对viewTree管理的必需逻辑。
ViewRootImpl类performTraversals()代码,源代码长的恐怖,这里给大家过滤一下
private void performTraversals() {
......
//lp.width和lp.height在创建ViewGroup实例时值为MATCH_PARENT
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
......
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
......
performLayout(lp, desiredWindowWidth, desiredWindowHeight);
......
performDraw();
......
}
//执行rootView的测量
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
try {
//ViewGroup的measure()方法
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
//执行layout操作
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
int desiredWindowHeight) {
mLayoutRequested = false;
mScrollMayChange = true;
mInLayout = true;
final View host = mView;
......
try {
//viewRoot先进行layout
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
mInLayout = false;
//需要layout的子view的数量
int numViewsRequestingLayout = mLayoutRequesters.size();
if (numViewsRequestingLayout > 0) {
//需要layout的子view
ArrayList<View> validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters,
false);
if (validLayoutRequesters != null) {
//如果view中有调用requestLayout()方法,则说明界面需要刷新
mHandlingLayoutInLayoutRequest = true;
int numValidRequests = validLayoutRequesters.size();
for (int i = 0; i < numValidRequests; ++i) {
final View view = validLayoutRequesters.get(i);
view.requestLayout()