在上篇博文DecorView绘制流程源码分析中,关于DecorView作为Activity、Window中的顶级View的绘制,我们已经作了一个详细的分析。但在具体说到View的绘制的时候,我们没有详细说明,所以本篇博文将会对View的绘制原理作深度分析。
在开始分析之前,我们需要了解一些概念,如:
- View:是所有UI组件的基类,是Android平台中用户界面体现的基础单位。
- ViewGroup:是容纳UI组件的容器,它本身也是View的子类。
- ViewRootImpl:是View的绘制的辅助类,所有View的绘制都离不开ViewRootImpl。
- MeasureSpec: View的内部类,主要就是View测量模式的工具类
一、View绘制三大流程分析
在DecorView的具体绘制中,我们涉及了View绘制的三大流程,具体分别为measure(测量)、layout(布局)和draw(绘制)。下面我们就来一一分析:
1.performMeasure(测量)
我们知道ViewRootImpl是View绘制的辅助类,View的绘制都是在ViewRootImpl的帮助下完成的,所以要了解View的measure(测量),我们就必须看看ViewRootImpl中的performMeasure()方法
在DecorView绘制流程源码分析中,我们知道mView就是DecorView,而DecorView继承于FrameLayout,FrameLayout又继承于ViewGroup,ViewGroup又继承于View,根据他们之间的关系,我们知道,mView.measure()是调用了父类View的measure()方法(因为只有View有measure方法),所以来分析View的measure()方法
此onMeasure()方法,在DecorView,Framelayout和View中都有定义,并且DecorView和FrameLayout重载了此方法,根据调用关系,这里调用了DecorView的onMeasure(),我们来看看此方法
关于测量模式,这里我们先不说,后面我们会说到,这里调用了父类FrameLayout中的onMeasure()方法,这里我们来看一下源码
由于FrameLayout是一个容器,可以装载其他的View,所以这里需要进行遍历其中的子View,并一一进行measure(测量)。
在具体说此方法前,我们先要了解一下View的测量模式及MeasureSpec类,具体我们先来看看MeasureSpec类的源码
这里主要通过位运算,来实现View的三种测量模式UNSPECIFIED、EXACTLY和AT_MOST。相关定义如下:
- UNSPECIFIED:父View不对子View有任何限制,子View需要多大就多大
- EXACTLY:父View已经测量出子View所需要的精确大小,这时候View的最终大小就是SpecSize所指定的值。对应于match_parent和精确数值这两种模式
- AT_MOST: 子View的最终大小是父View指定的SpecSize值,并且子View的大小不能大于这个值,即对应wrap_content这种模式。
我们继续上面FrameLayout的onMeasure()方法继续分析,可以发现,此方法主要就是组装子View宽高的测量规格MeasureSpec,然后作为参数传给子View的measure()方法。这里我们只来看一个组装就好,我们来看宽的组装的测量规格MeasureSpec,我们来看看相关代码
当子View的布局参数lp.width为LayoutParams.MATCH_PARENT时,生成后的测量规格MeasureSpec是以测量模式为MeasureSpec.EXACTLY的值。当lp.width不为LayoutParams.MATCH_PARENT时,这是调用了ViewGroup中的getChildMeasureSpec()方法,来生成相关值,这里我们来看此方法
这里主要传入了FrameLayout的测量规格MeasureSpec,根据FrameLayout的测量规格和子View具体的padding和childDimension值,从而决定子view宽的测量规格MeasureSpec。具体创建细节,这里就不说。回到FrameLayout的onMeasure()方法,这样当子View的宽高的测量规格都计算出来之后,就会调用子View的measure()方法。如果子View不再是ViewGroup,那样就会调用子View(或自定义View)的onMeasure()方法,从而完成View的测量;如果子View还是ViewGroup,那就会按我们说的逻辑再走一遍measure方法。
2.performLayout(布局)
说完View绘制的measure(测量),我们来看看View绘制的layout(布局)。同样的,我们先来看ViewRootImpl中的performLayout()方法
由相关继承类的关系,我们知道,这里调用的是View的layout()方法
通过源码我们知道,ViewGroup是一个抽象的View的子类,而FrameLayout是ViewGroup的实现类,所以这里onLayout()是FrameLayout中的方法,我们来看一下此方法
这里主要就是通过padding和margin算出子View的top,left,bottom,right四个顶点的值,从而再调其子View的layout方法。如果子View child不是ViewGroup,最后直接调用子View(或自定义View)的onLayout()方法,如果child是ViewGroup,那就再走一遍流程。
3.performDraw(绘制)
从DecorView绘制流程源码分析中,我们知道performDraw()绘制有两种方式,分别为Hardware渲染(硬件加速)和Software渲染,因为两种绘制方式最后也都走到调用View的draw()方法,所以这里我们来看看software渲染方式的绘制
同上,通过分析知,这里mView.draw(canvas)其实是调用View.draw(canvas)方法,让我们来看看此方法
从此方法,我们知道View的draw()分五步,分别为:
- 第一步,如果有背景,绘制背景
- 第二步,保存画布的层级
- 第三步,绘制内容
- 第四步,分发绘制子View
- 第五步,绘制fade效果和restore Layers
由于我们的DecorView是FrameLayout,是ViewGroup,所以我们来看一下第四步,分发绘制子View,来看ViewGroup中dispatchDraw()方法(此方法主要是ViewGroup中实现)
通过遍历ViewGroup中的子View,然后在调用子View的draw方法,这里draw()方法和我们前面的view的draw()有点不一样,因为是三个参数的,我们再来看此方法
可以发现,最后还是调用回了View的draw(canvas)方法。所以对于View的draw(绘制),其实也和measure(测量)和layout(布局)一样,如果View是ViewGroup,就是在draw的时候会进行分发绘制子View,如果view就是View,那就会调用View(或自定义View)的onDraw()方法,绘制内容。
到这里,我们View的三大绘制原理就分析完了。
注:源码采用android-4.1.1_r1版本,建议下载源码然后自己走一遍流程,这样更能加深理解。