Learn && Live
虚度年华浮萍于世,勤学善思至死不渝
前言
Hey,欢迎阅读Connor学Android系列,这个系列记录了我的Android原理知识学习、复盘过程,欢迎各位大佬阅读斧正!原创不易,转载请注明出处:http://t.csdn.cn/Vt8Re,话不多说我们马上开始!
1.View绘制流程
View的绘制流程是从ViewRoot的performTraversals方法开始的,经过measure、layout、draw三个过程才最终将一个View绘制出来
measure用来测量View的宽和高,完成后可通过getMessuredWidth / Height获取View测量后的宽 / 高
layout用来确定View在父容器中的位置,决定View的四个顶点的坐标和实际的View的宽和高,可通过对应的get方法获取
draw负责根据前面获得的View尺寸、位置将View绘制在屏幕上
measure过程
(1)performMeasure()会调用最外层ViewGroup的measure(),在其内调用onMeasure()。
(2)ViewGroup的onMeasure()中会调用measureChildren(),用于遍历子View然后循环调用 measureChild()
(3)measureChild()中会用先取出子元素的LayoutParams,再通过getChildMeasureSpec(),根据父容器的MeasureSpec + 当前View的LayoutParam + View的margin和padding值,创建当前View的MeasureSpec
(4)然后将MeasureSpec传递给当前View的子元素的measure(),在其内调用onMeasure()
(5)View的onMeasure()内会调用setMeasureDimension(getDefaultSize(),getDefaultSize()),getDefaultSize(),默认返回measureSpec的测量数值
(6)重复上述的循环调用直至当前View没有子元素,即可完成从顶级View到普通View的measure过程
layout过程
(1)performLayout()会调用最外层ViewGroup的layout(),大致流程如下
- 通过setFrame()来设定View的四个顶点位置
- 调用onLayout()确定子元素的位置
(2)onLayout()的实现逻辑与onMeasure类似,遍历子元素,循环调用setChildFrame() → 子元素的layout(),最终确定顶级View到普通View的位置
draw过程
(1)performLayout()会调用最外层ViewGroup的draw()方法,再经历如下的绘制流程
(2)绘制背景background.draw(canvas)
(3)绘制自己(onDraw)
(4)绘制子元素(dispatchDraw)
(5)绘制装饰(onDrawSrollBars)
(6)循环调用最终绘制出所有的View
View的测量宽高和最终宽高有什么区别?
在View的默认实现中,View的测量宽高和最终宽高是相等的,区别在于测量宽高形成于measure过程,而最终宽高形成于layout过程
以下两种情况可能会导致两者不相等:
(1)在layout中调用super.layout()对最终宽高做修改
(2)在某些情况下,View需要多次measure才能确定自己的测量宽高,在前几次的测量过程中,得出的结果可能和最终宽高不一致,但最终两者还是相同的
2.自定义View
(1)继承View重写onDraw():实现的效果不好通过布局方式实现,静态或动态显示一些不规则图形,如:Canvas绘制图形,需要自己支持wrap_content并处理padding
(2)继承ViewGroup派生特殊的Layout:实现自定义布局,需要合理地处理ViewGroup和其子元素的的测量、布局过程
(3)继承特定的View(如TextView):扩展已有View的功能,如自定义字体的TextView、带有删除按钮的EditText,不需要自己支持wrap_content并处理padding
(4)继承特定的Layout:扩展已有布局,不需要自己处理ViewGroup和其子元素的的测量、布局过程