View的绘制流程:OnMeasure()——>OnLayout()——>OnDraw()
View的绘制过程就是从ViewRoot的performTraversals方法开始的,它经过measure、layout、draw三个过程才能最终将一个View绘制出来:
1.measure用来测量View的宽和高。
2.layout用来确定View在父容器中放置的位置。
3.draw用来将view绘制在屏幕上。
大致的流程如下图所示:
如图所示,performTraversals会依次调用performMeasure、performLayout和performDraw三个方法,这三个方法分别完成顶级View的measure、layout、draw这个三个流程。其中:
1.perfromMeasure中会调用measure方法,在measure方法中又会调用onMeasure方法,在onMeasure方法中会对所有的子元素进行measure过程,这个时候measure流程就从父容器传递到子元素中了,这样就完成了一次measure过程。接着子元素会重复父元素的measure过程,如此反复就完成整个View树的遍历。
view的绘制流程是自上而下再自下而上的流程。父元素将自身的size长宽度和测量模式传递给子view,子view根据父元素确定自身的size长宽度和测试模式。接着子元素又将自身的长宽度和测量模式传递给子view,如此反复就完成整个View树的遍历。如果父元素是固定的size或match_parent,那么子元素随即可以确定size(长度和宽度)和测量模式为Exactly。如果父元素是wrap_content,那么就要根据子元素的size返回给父元素,父元素才能确定自身的size。
2.performLayout的传递流程和performMeasure是一样的。
3.performDraw的传递过程是在draw方法中通过dispathDraw来实现的,本质上并没有区别。
二、自定义view中重写的onMeasure方法为何只有两个参数,而不是四个参数(长度、宽度、长度测量模式、宽度测量模式)?
因为参数太多会占用内存。
所以这两个参数并不是实际的长度和宽度。而是长度和测试模式的拼接。java 中 int 类型数据占用 32 位,包括符号位和幅度位。这里的参数就是最高两位是测量模式(00、01、11代表三种测量模式),剩下30位存放实际的长度或宽度。
三、Mea和sureSpec
MeasureSpec的中文意思是测量规格的意思,MeasureSpec代表一个32的int值,高2位代表SpecMode测量模式,低30位代表SpecSize测量规格大小。
经常使用的三个函数:
1、public static int makeMeasureSpec(int size,int mode) // 构造一个MeasureSpec
2、public static int getMode(int measureSpec) // 获取MeasureSpec的测量模式
3、public static int getSize(int measureSpec) // 获取MeasureSpec的测量大小
SpecMode分为三类,每一类都没有他们对应的约束。
1、UNSPECIFIED
父容器不对View有任何限制,要多大就给多大,这种情况,一般我们自定义View用不到。
2、Exactly
这个表示准确值,这个对应着LayoutParams中的matchparent和准确值,这个时候View的最终大小就是SpecSize所指定的数。
3、AT_MOST
父容器指定了一个可用大小即SpecSize,View的大小不能大于该值,具体是什么要看View中自己的处理,所有自定义View时,我们需要自己在measure里面处理设置布局的大小,它对应layoutparams中的wrap_content
四、View的绘制流程是从Activity的哪个生命周期方法开始执行的
onResume方法执行之后开始。