在本篇文章里小编给大家整理的是关于Android UI绘制流程及原理以及相关知识点,需要的朋友们可以学习下。
一、绘制流程源码路径
1、Activity加载ViewRootImpl
2、ViewRootImpl启动View树的遍历
二、View绘制流程
1、measure
(1)MeasureSpec是什么?
重写过onMeasure()方法都知道,测量需要用到MeasureSpec类获取View的测量模式和大小,那么这个类是怎样存储这两个信息呢?
留心观察的话会发现,onMeasure方法的两个参数实际是32位int类型数据,即:
00 000000 00000000 00000000 00000000
而其结构为 mode + size ,前2位为mode,而后30位为size。
==> getMode()方法(measureSpec --> mode):
getSize()方法同理。
==> makeMeasureSpec()方法(mode + size --> measureSpec):
这里解释一下,按位或左侧为size的高2位清零后的结果,右侧为mode的低30位清零后的结果,两者按位或运算的结果正好为高2位mode、低30位size,例:
二进制计算规则可参考:
==> 测量模式:
UNSPECIFIED:父容器不对View作任何限制,系统内部使用。
EXACTLY:精确模式,父容器检测出View大小,即为SpecSize;对应LayoutParams中的match_parent和指定大小的情况。
AT_MOST:最大模式,父容器指定可用大小,View的大小不能超出这个值;对应wrap_content。
(2)ViewGroup的测量流程
回到ViewRootImpl的performMeasure方法,这里传入的参数为顶层DecorView的测量规格,其测量方式为:
match_parent和具体数值大小为EXACTLY模式,wrap_content则为AT_MOST模式。
往下走,performMeasure方法中调用了DecorView的onMeasure方法,而DecorView继承自FrameLayout,可以看到FL的onMeasure方法中调用了measureChildWithMargins方法,并传入自身的测量规格:
即测量子控件的大小,测量规则详情可看getChildMeasureSpec方法,总结如下:
childLayoutParams\parentSpecMode | EXACTLY | AT_MOST | UNSPECIFIED |
---|---|---|---|
dp | EXACTLY/childSize | EXACTLY/childSize | EXCATLY/childSize |
match_parent | EXACTLY/parentSize | AT_MOST/parentSize | UNSPECIFIED/0 |
wrap_content | AT_MOST/parentSize | AT_MOST/parentSize | UNSPECIFIED/0 |
回到onMeasure方法,测完子控件之后,ViewGroup会经过一些计算,得出自身大小:
综上,ViewGroup的测量需要先测量子View的大小,而后结合padding等属性计算得出自身大小。
(3)View的测量流程
可以看到setMeasuredDimensionRaw()方法:
View不需要考虑子View的大小,根据内容测量得出自身大小即可。
另外,View中的onMeasure方法中调用到getDefaultSize方法:
这里看到精确模式和最大模式,最终测量的结果都是父容器的大小,即布局中的wrap_content、match_parent以及数值大小效果都一样,这也就是自定义View一定要重写onMeasure方法的原因。
2、layout
布局相对测量而言要简单许多,从ViewRootImpl的performLayout方法出发,可以看到其中调用了DecorView的layout方法:
进入layout方法,发现l、t、r、b被传递到了setFrame方法中,并设置给了成员变量:
所以,布局实际为调用View的layout方法,设置自身的l、t、r、b值。另外,layout方法中往下走,可以看到调用了onLayout方法,进入后发现为空方法。因而查看FrameLayout的onLayout方法:
可以看到,进行一系列计算后,调用了child的layout方法,对子控件进行布局,同时子控件又会继续往下对自己的子控件布局,从而实现遍历。
综上,布局实际为调用layout方法设置View位置,ViewGroup则需要另外实现onLayout方法摆放子控件。
3、draw
(1)绘制过程入口
(2)绘制步骤
进入到View的draw方法中,可以看到以下一段注释:
结合draw方法的源码,绘制过程的关键步骤如下:
- ==> 绘制背景:drawBackground(canvas)
- ==> 绘制自己:onDraw(canvas)
- ==> 绘制子view:dispatchDraw(canvas)
- ==> 绘制滚动条、前景等装饰:onDrawForeground(canvas)
感谢大家的阅读和对编程学习网的支持。