Android View 的工做原理(包含对 DecorView 和 ViewRoot 的简单介绍)

    什么是 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 这个标志位。  

相关文章
相关标签/搜索