View的绘制流程

一 View 的绘制流程
之前,我们先了解DecorView和ViewRoot(ViewRootImpl)。
DecorView作为*View,包含2个部分,标题栏(默认情况下,不设置去掉标题栏主题,APP程序最上部的titleBar)和内容栏(通过setContentView所设置的布置文件被加到内容栏中)
ViewRoot对应于ViewRootImpl类,它是连接WindowManager和DecorView的纽带。View的三大流程均是通过ViewRoot来完成的。在ActivityThread 中,当Activity对象创建完毕后,会将DecorView添加到Window中,如图(API26中ActivityThread中handleResumeActivity方法):
View的绘制流程
同时会创建ViewRootImpl对象,并将ViewRootImpl对象和DecorView建立关联,如下图所示(API-26中WindowManagerGlobal类addView方法):
View的绘制流程
为什么通过WindowManagerGlobal调用呢?
View的绘制流程
WindowManagerImpl 是WindowManager的实现类,WindowManager继承ViewManagr,在WindowManagerImpl中addView方法中调用了WindowManagerGlobal 的addView方法 ,因此 ViewRootImpl对象和DecorView建立了联系

在setView方法中发现requestLayout方法,
View的绘制流程
翻译的意思大概是: 安排第一个布局-before-添加到窗口管理器,以确保我们在从系统接收任何其他事件之前进行重新布局。
进入requestLayout()中 ,依次执行scheduleTraversals(),doTraversal(), performTraversals()方法,在 performTraversals()中发现,measure,layout,draw 方法,所以View的绘制流程是从ViewRoot的performTraversals方法开始的。
其中在performMeasure中会调用measure方法(当前View),在measure方法中又会调用onMeasure方法,在onMeasure方法中则会对所有子View进行measure过程(子view),此时,measure流程就从父容器传递到子元素中,完成一次measure过程。接着子元素会重复父容器的measure过程,…..,如此就完成了View树的遍历
View的绘制流程
同理,performLayout和performDraw是类似的,不过,performDraw的传递过程是在其中的draw方法中,通过dispatchOnDraw来实现的,没有本质区别
View的绘制流程
翻译意思:通知已注册监听绘图过程即将开始
那View的draw方法在哪里调用呢?
接着走,来到了
View的绘制流程
在该方法里,看到了Canvas以及View 的draw方法,在draw方法中执行了绘制所需步骤
View的绘制流程
至此,performTraversals结束
二 MeasureSpec
2.1 MeasureSpec的理解
MeasureSpec 参与了View的measure过程,在测量过程中,系统会将View的LayoutParams根据父容器施加的规则转换成对应的MeasureSpec
MeasureSpec 代表一个32位int 值,前2位代表SpecMode,后30位代表SpecSize。SpecMode代表测量的模式,SpecSize值在某种测量模式下的规格大小。
SpecModel有三种测量模式:
1. EXACTLY: 父容器已经检测出子View所需要的精确大小,这个时候view的大小即为SpecSize的大小(对应于布局参数中的MATCH_PARENT,或者具体大小值)
2.AT_MOST: 父容器指定了一个大小,即SpecSize,子view的大小不能超过这个SpecSize的大小 (对应于LayoutParams 中的wrap_content)
3.UNSPECIFIED: 父容器不对子View有任何的约束,要多大给多大,一般用户系统内部,例如:ListView,ScrollView等(自定义控件中一般不会用到这个测量模式的)。
2.2 与LayoutParams的关系
在View测量的时候,系统会将LayoutParams在父容器的约束下转换成对应的MeasureSpec,然后根据这个MeasureSpec来确定View测量后的宽/高。
对于顶层View(DecorView),其MeasureSpec由窗口的尺寸和其自身的LayoutParams来共同决定,对于普通View,其MeasureSpec由父容器的MeasureSpec和自身的LayoutParams来共同决定

参考
1. 《Android开发艺术探索》
2.https://www.jianshu.com/p/9da7bfe18374