什么是 View ?布局
View 是 Android 中全部控件的基类,View 能够是单个控件,也能够是由多个控件组成的一组控件。ViewGroup 里面能够有子 View,子 View 里面也能够有 ViewGroup。优化
什么是 ViewRoot、DecorView ?spa
View 有三大流程,measure、layout、draw,了解并熟悉其三大流程对于咱们进行 Android 开发有着极其重要的做用。在熟悉三大流程以前,先介绍一下 ViewRoot 和 DecorView 的概念。orm
Activity 内部 是组合了一个 Window 对象,Activity 全部的事件都是交给 Window 来处理的,可是 Window 是一个抽象类,处理事务具体的操做就只能交给它的实现类,也就是 PhoneWindow,PhoneWindow 就会把事件传给这个 DecorWindow,这个 DecorWinow 是应用窗口的根容器,也就是个顶级 View,实际上是个 FrameLayout。这个根容器通常状况下都只有惟一一个子 View ,就是一个垂直的 LinearLayout,上下两部分分别是标题栏和内容栏。而 ViewRoot 是 View 树的管理者,它的成员变量 mView 对应的就是一个布局的根 View,ViewRoot 是 View 和 WindowManger 的桥梁,也就是 DecorView 和 WindowManger 的纽带,对应的是 ViewRootImpl 类。当 Activity 建立完成后,就会建立 ViewRootImpl 对象,将 DecorView 添加到 Window 中,并将 DecorView 和 ViewRootImpl 创建关联。对象
View 的三大流程就是从 ViewRoot 的 performTraversals 方法开始的。该方法会依次调用 performMeasure、performLayout、performDraw 三个方法,来完成顶级 View 的三大流程。三个方法又会分别调用 onMeasure、onLayout、onDraw 方法来完成对子 View 的 measure、layout、draw。接着子元素会重复父容器的 measure、layout、draw。来完成整个 View 树的三大流程。measure 决定 View 的 宽高、layout 肯定四个顶点的位置、draw 则会将内容呈如今屏幕上。继承
什么是 MeasureSpec ?事件
为了更好的了解 View 的测量过程,咱们还须要了解 MeasureSpec。MeasureSpec 在很大程度上决定了一个 View 的尺寸规格。若是对于 DecorView,其 View 的 MeasureSpec 就是由窗口的大小和自身的LayoutParams 所决定的,可是若是是一个子 View,其 MeasureSpec 则是由父容器的 MeasureSpec 和自身的 LayoutParams 所共同决定的。事务
MeasureSpec 是一个32位 int 值,高两位表明的是测量模式 SpecMode,低30位表明的是某种测量模式下的规格大小 SpecSize。开发
SpecMode 有三类:UNSPECIFIED、EXACTLY、AT_MOST。get
UNSPECIFIED:父容器不对子 View 作限制,想要多大就多大。
EXACTLY:父容器检测出 View 的精确大小,这个时候 View 的最终大小就是 SpecSize 所指定的值。对应的是 LayoutParams 中的 match_parent 或 具体数值。
AT_MOST:父容器指定一个大小 SpecSize,子 View 的大小按照自身要求决定,可是不能超过 SpecSize,对应 LayoutParams 中的 wrap_content。
View 的三大流程
measure 过程:
measure 过程要分清状况,当要测量的只是一个原始的 View 时,那么只经过 measure 就能够完成其测量过程,可是若是是一个 ViewGroup,就须要遍历测量全部的子 View。
下面是 View 的 onMeasure 方法:
它调用了 setMaasureDimension() 方法来设置 View 宽高的测量值,那么 getDefaSize() 方法又是干吗的呢?
它是经过测量模式来返回测量得出的值。当测量模式为 UNSPECIFIED 时,测量值就为 result,也就是 getSuggestedMinimumWidth() 或者 getSuggestedMinimumHeight() 返回的值。当测量模式为 AT_MOST 和 EXACTLY 时,返回的大小就是 MeasureSpec 中测量获得的 SpecSize。
那么 getSuggestedMinimumWidth() 和 getSuggestedMinimumHeight() 方法又在干什么呢?
这两个方法会判断当前 View 的背景是否是为空,为空就返回 mMinWidth/mMinHeight(即为 minWidth 和 minHeight 属性设置的值),不为空就返回 mMinWidth/mMinHeight 和 背景的 getMinimumWidth()/getMinimumHeight() 返回值中的较大的那个值。
而 ViewGroup 的 measure 过程以下:
对于 ViewGroup 来讲,不只须要测量自身,还须要测量全部的子 View。和 View 不一样的是,ViewGroup 是一个抽象类,因此没有重写 View 的 onMeasure() 方法,而是提供了 measureChildren() 的方法。
该方法会遍历 ViewGroup 的每个 Child,而后调用 measureChild() 方法。
该方法会获得子元素的 LayoutParams,再经过 getChildMeasureSpec 来获取子元素的 MeasureSpec,而后调用 View 的 measure() 方法进行测量。
layout 过程:
layout 过程是为了肯定 View 的位置,在 ViewGroup 位置被肯定后,它会遍历并肯定全部子元素的位置。
下面是 View 的 layout() 方法:
layout() 方法首先会经过 setFrame() 方法来设定四个顶点的位置,四个顶点一旦肯定,View 在父容器中的位置也就肯定了,接着会调用 onLayout() 方法。这个方法是父容器肯定子元素的位置,由于布局选择的不一样,确认的方式也会不同,因此在 View 和 ViewGroup 中都没有真正实现 onLayout()。具体的 onLayout() 实现要看选择的布局方式。
ViewGroup 中,会使用 onLayout() 方法遍历全部子元素并调用其 layout() 方法,在 layout() 方法中又会调用 onLayout() 方法,直到肯定完整个 View 树的位置。
draw 过程:
draw 过程是将 View 绘制到屏幕上,View 源码中的 draw() 方法比较长,就不复制到这了,感兴趣的小伙伴能够本身去看看。
draw() 方法主要有四个部分:绘制背景、绘制本身、绘制 children、绘制装饰。
绘制过程的传递是经过 dispatchDraw() 方法来实现的,会遍历全部子元素的 draw() 方法,一层一层绘制完整个 View 树。
View 还有一个特殊方法,setWillNotDraw(),以下:
表示若是 View 不须要绘制任何东西时,会将 flags 设置为 true,而后进行相应的优化。默认状况下,View 没有启用这个优化标志位,可是 ViewGroup 默认启用了。当一个自定义 View 继承自 ViewGroup 而且自己不须要绘制时,能够开启这个标志位方便后续优化,若是须要绘制,就得显性关闭 WILL_NOT_DRAW 这个标志位。