概述
关于View的绘制我阅读过的所有博文都很长,一篇文章看下来至少要花两三个小时,才能看个大概,包括其中的源码分析等。今天这篇文章主要是针对View的绘制做下大概的汇总,不会很详细,网上关于View的绘制流程详细介绍有很多,大家自行百度吧。这篇文章的目的是假如有人问你的时候,比如面试官,你能用简单的话语介绍给他听,尽管是简答,但是该有的还是要有。
介绍几个View,结构如上图所示:
- PhoneWindow:将Decoriew设置为整个应用窗口的根View,是Window的实现类,它是Android中的最基本的窗口;
- DsecorView:又叫顶级View,系统会将要显示的具体内容呈现在PhoneWindow上。一般情况下,它的内部会包含一个竖直方向的LinearLayout,在这个layout中有两个部分:title和content,DecorView可以通过View view = getWindow().getDecorView()获得。它还是PhoneWindow与ViewRoot之间的桥梁,ViewRoot会通过DecorView设置窗口属性;
- DecorView中的TitleView:标题栏,一般指的是toolbar。
- DecorView中的ContentView:内容栏,是一个id为content的Framelayout,我们平时所写的setContentView()其实就是在设置它。
源码分析
通常我们自定义View的时候可能会用到view.setBackgroundColor(),或是布局中添加View(view.addView()),这些都能引发View的绘制。并且这些都是依附于Activity的,也就是说没有Activity就没有View的绘制。看下Activity的生命周期回调
ActivityThead.class:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22// H();
case RESUME_ACTIVITY://Acticity生命周期Resume
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityResume");
SomeArgs args = (SomeArgs) msg.obj;
handleResumeActivity((IBinder) args.arg1, true, args.argi1 != 0, true,
args.argi3, "RESUME_ACTIVITY");
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
// 调用onResume方法
r = performResumeActivity(token, clearHide, reason);
// 将decorView添加到屏幕中
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
//wm其实就是WindowManagerImpl,而在WindowManagerImpl中又调用了WindowManagerGlobal的addView方法。
//将DecorView添加到Window中
wm.addView(decor, l);//调用AddView,开始View的绘制
}
了解到Activity的生命周期回调原理,我们知道View的绘制就是在onResume中开始的。
wm其实就是WindowManagerImpl,而在WindowManagerImpl中又调用了WindowManagerGlobal的addView方法。
将DecorView添加到Window中,同时创建ViewRootImp对象
WindowManagerGlobal.java:1
2
3
4
5
6
7
8
9
10
11
12
13// WindowManagerGlobal.java
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
ViewRootImpl root;
root = new ViewRootImpl(view.getContext(), display);//构建ViewRootImpl,这个类非常的关键
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
//创建ViewRootImp对象,并将ViewRootImp和DecorView建立关联
root.setView(view, wparams, panelParentView);//设置View
}
以上前期的准备工作完成后,
View的绘制流程是从ViewRoot(ViewRootImpl)的performTraversals()方法开始的
ViewRootImpl.java:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
requestLayout();
}
public void requestLayout() {//我们在自定义ViewGroup的时候,如果要更新布局就会调用这个方法刷新界面,其实就是调用了View的重新绘制流程。
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
void scheduleTraversals() {
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
final class TraversalRunnable implements Runnable {
public void run() {
doTraversal();
}
}
void doTraversal() {
performTraversals();
}
//View的绘制再此开始
private void performTraversals() {
// ... ...
// Ask host how big it wants to be
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
// ... ...
performLayout(lp, mWidth, mHeight);
// ... ...
performDraw();
}
说是简答,感觉越写越长。。。刹不住车了
View的绘制流程是从 ViewRoot 的 performTraversals 方法开始的,它经过 measure、layout、draw三个过程才最终将一个View绘制出来,performTraversals会依次调用 performMeasure,performLayout和 performDraw 三个方法,这三个方法分别会完成View的measure、layout、draw的流程。例如performMeasure会调用measure方法,measure方法中又会调用onMeasure方法,在onMeasure方法中会对所有子view进行measure过程,子view再重复此过程,如此反复就实现了view树的遍历。参数:(widthMeasureSpec,heightMeasureSpec)高2位表示MODE,低30位表示size。当然在measure、layout、draw中还有许多的细节,这里就不讲了。
总结一下
准备阶段
启动Activity —> 创建一个PhoneWindow —> getWindow().setContentView(id)下初始化DecorView —> onRsume()下将DecorView添加到Window中,同时创建ViewRootImp对象 —> performTraversals()
绘制阶段
performTraversals()会依次调用performMeasure、performLayout和performDraw ,然后就是各种遍历计算,绘制子view。