View的绘制流程



1.ViewRoot:
ViewRoot对应于ViewRootImpl类,Android2.2以后用ViewRootImpl替代了它。它是连接WindowManager和DecorView的纽带,View的三大流程都是通过ViewRootImpl类来完成的。
在ActivityThread中,当Activity对象被创建完毕后,会将DecorView添加到Window中,同时会创建ViewRootImpl对象,并将ViewRootImpl对象和DecorView建立关联。
View的绘制流程是从ViewRootImpl的performTraversals方法开始的,他经过measure,layout和draw三个过程才能最终将一个View绘制出来,其中measure用来测量View的宽和高,layout用来确定View在父容器的放置位置,而draw则负责将View绘制在屏幕上。流程图如下:

peformTraversals会依次调performMeasure,performLayout和performDraw三个方法,这三个方法分别完成顶级View的
measure,layout和draw这三大流程,其中在performMeasure中会调用mesure方法,在mesure方法中又会调用onMeasure方法,在onMeasure方法中则会对所有的子元素进行measure过程,这个时候measure流程就从父容器传递到子元素中了,这样就完成了一次measure过程。接着子元素会重复父容器的measure过程,如此反复就完成了整个View树的遍历。其他两个类似。
1)measure过程决定了View的宽/高,Measure完成以后,可以通过getMeasuredWidth/getMeasuredHeight方法来获取到View测量后的宽/高,基本它都等于View最终的宽/高,特殊情况除外。
2)Layout过程决定了View的四个顶点的坐标和实际的View的宽、高。完成以后可以通过getTop、getBottom、
getLeft、getRight来拿到View的四个顶点的位置,并可以通过getWidth和getHeight方法来拿到View的最终宽/高。
3)Draw过程则决定了View的现实,只有draw方法完成以后View的内容呈现在屏幕上。

2.DecorView


DecorView作为顶级View,它包含一个竖直方向的LinearLayout,在这个LinearLayout里面有上下两个部分,上面是标题栏,下面是内容栏(由FrameLayout组成)。在Activity中setContentView所设置的布局文件其实就是加在内容栏之中。内容栏的id是content,获取content对象:
ViewGroup content =  (ViewGroup)findViewByID(android.R.id.content);

获取设置的View:
View view = content.getChildAt(0);

View层的事件都是先经过DecorView,然后才传递给我们的View。

3.MeasureSpec
在measure过程中,系统会将View的LayoutParams根据父容器所施加的规则转换成对应的MeasureSpec,然后再更具这个measureSpec来测量出View的宽/高,当然这个宽和高不一定就等于View最终的宽和高。
MeasureSpec代表一个32位int值,高2位代表SpecMode,是指测量模式,而SpecSize是指在某种测量模式下的规格大小。
MeasureSpec通过将SpecMode和SpecSize打包成一个int值来避免过多的对象内存分配,MeasureSpec提供了打包盒解包方法。这里,三个数据都是int类型。
SpecMode有三类,每一类都表示特殊的含义
1)UNSPECIFIED
父容器不对View有任何限制,要多大给多大,这种情况一般用于系统内部,表示一种测量的状态。
2)EXACTLY
父容器以及检测出View所需要的精确大小,这个时候View的最终大小就是SpecSize所指定的值。
3)AT_MOST
父容器指定了一个可用大小即SpecSize,View的大小不能大于这个值,具体是什么值要看不同View的具体实现。它对应于LayoutParams中的warp_content

(1)MeasyreSpec和LayoutParams的对应关系
在View测量的时候,系统会将LayoutParams在父容器的约束下转换成对应的MeasureSpec,然后再根据MeasureSpec来确定View测量后的宽/高。对于顶级View和普通View来说,MeasureSpec的转换过程略有不同。
对于DecorView,其MeasureSpec由窗口的尺寸和其自身的LayoutParams来共同确定;
对于普通View,其MeasureSpec由父容器的MeasureSpec和自身的LayoutParams来共同决定.
MeasureSpec一旦确定,onMeasure就可以确定View的测量宽、高。
1)对于DecorView来说,它的View是在ViewRootImpl中的getRootMeasureSpec方法实现的。它遵循如下的规则,根据它的LayoutParams中的宽/高的参数来划分:

LayoutParams.MATCH_PARENT:精确模式,大小就是窗口的大小;
LayoutParams.WRAP_CONTENT:最大模式,大小不定,但是不能超过窗口的大小;
固定大小(比如100dp):精确模式,大小为LayoutParmas中指定的大小。

2)View的measure过程是由ViewGroup传递而来,ViewGroup的measureChildWithMargins方法会对子元素进行measure,在调用子元素的measure方法之前会通过getChildMeasureSpec方法来得到子元素的MeasureSpec。在此方法子元素的MeasureSpec的创建主要与父容器的MeasureSpec和子元素本身的LayoutParams有关,此外还和View的margin以及padding有关。

4.View的工作流程
View的工作流程主要是指measure、layout、draw这三大流程,即测量、布局和绘制。
Measure过程要分情况来看,如果只是一个原始的View,那么通过measure方法就完成了其测量过程;如果是一个ViewGroup,除了完成自己的测量过程外,还会遍历去调用所有子元素的measure方法,各个子元素再递归去执行这个流程。
(1)View的measure过程
View的measure过程由measure方法来完成,measure方法是一个final类型的方法,这意味着子类不能重写这个方法,measure中调用了onMeasure方法。

setMeasuredDimension方法会设置View宽、高的测量值,接下来主要看它的参数,getDefaultSize这个方法:

getDefaultSize这个方法其实就是返回的是measureSpec中的specSize,而这个SpecSzie就是View测量后的大小,View最终的大小是在layout阶段确定的,但是几乎所有的情况下View的测量大小和最终大小是相等的。
因此,直接继承View的自定义控件需要重写onMeasure方法并设置wrap_content时的自身大小,否则在布局中使用的wrap_content就相当于使用match_parent。因为如果View在布局中使用wrap_content,那么它的specMode是AT_MOST模式,这种情况下View的specSize是parentSize,这是不合理的。所有在重写时,我们要指定一个默认的内部宽和高给wrap_content.


(2)ViewGroup的measure过程
ViewGroup除了完成自己的measure过程以外,还会遍历去调用所有子元素的measure方法,各个子元素再去递归这个过程。和View不同的是,ViewGroup是一个抽象类,没有重写View的onMeasure方法,但是它提供了一个叫measureChildren的方法。ViewGroup在measure时候,会对每一个子元素进行measure。在measureChild方法中,会取出子元素的LayoutParams,然后通过getChildMeasureSpec来创建子元素的MeasureSpec,接着将MeasureSpec直接传递给View的measure方法进行测量。
但是ViewGroup并没有定义其测量的具体过程,因为他是一个抽象类,具体的测量过程onMeasure方法需要其各个子类去具体实现。

(2)Layout过程
Layout的作用是ViewGroup用来确定子元素的位置,当ViewGroup的位置被确定后,它在onLayout中会遍历所有的子元素并调用其layout方法,在layout方法中onLayout方法又会被调用。Layout方法确定View本身的位置,而onLayout方法则会确定所有子元素的位置。

1、View通过layout方法来确认自己在父容器中的位置
2、 ViewGroup通过onLayout 方法来确定View在容器中的位置

(3)draw过程
Draw过程比较简单,它的作用就是将View绘制到屏幕上面,View的绘制过程遵循如下几步:
1)绘制背景background.draw(canvas)
2)绘制自己(onDraw)
3)绘制children(dispatchDraw)
4)绘制装饰(onDrawScrollBars)

参考:Android开发艺术探索



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值