View的绘制过程就是从ViewRoot的performTraversals方法开始的,它先后经过measure、layout、draw三个过程才能最终将一个View绘制出来。分别对应onMeasure()、onLayout()和onDraw()这三个方法。下面分别谈一下这三个方法。
measure
measure是用来测量view的宽和高。在measure方法中又会调用onMeasure方法,在onMeasure方法中会对所有的子元素进行measure过程,这个时候measure流程就从父容器传递到子元素中了
measure()方法接收两个参数,widthMeasureSpec和heightMeasureSpec,这两个值分别用于确定视图的宽度和高度的测量规格。MeasureSpec代表一个32的int值,高2位代表SpecMode测量模式,低30位代表SpecSize测量规格大小。SpecMode 分为三类
1.EXACTLY(完全)
父容器完全决定view的大小,子view的大小被忽略,被限定在给定的边界里。
2.AT_MOST(至多)
子view最多达到给定大小的值
3.UNSPECIFIED(未指定)
父容器不对view的大小做任何限制,子view可以得到任意想要的大小
在setMeasuredDimension()方法调用之后,我们才能使用getMeasuredWidth()和getMeasuredHeight()来获取视图测量出的宽高,以此之前调用这两个方法得到的值都会是0。
layout
layout方法是在measure方法结束后进行,此时view的大小已经测量好了。layout的作用是ViewGroup用来确定子元素的位置,当ViewGroup的位置被确定后,它在onLayout中会遍历所有的子元素并调用Layout方法,在layout方法中onLayout方法又会被调用。这样一层一层的完成所有子View的布局过程。layout里面通过setFrame来确定View的四个顶点的位置,然后再layout中调用onLayout,这个方法用来确定子View的位置,起到一层层传递的作用。
draw
draw方法是在layout方法结束后进行,顾名思义,这里是绘制view的地方。draw主要分为6步。
1.绘制view的背景图
2.如果要视图显示渐变框,开始准备
3.调用onDraw()函数,绘制视图本身。在view中进入onDraw()方法会发现是个空方法,因为每个子类视图的内容部分都是各不相同的,所以需要在子类中去重载,自己绘制
4.绘制子视图,调用dispatchDraw()。如果视图中没有子视图,就不需要绘制了。因此你会发现View中的dispatchDraw()方法又是一个空方法,而ViewGroup的dispatchDraw()方法中就会有具体的绘制代码
5.如果要视图显示渐变框,这里开始绘制
6.绘制滚动条
显然 第2和第5步基本是用不到的。重要的是第3步。