UI绘制流程
在上一篇文章自定义控件中,其实已经比较全面的介绍了自定义控件的知识,在这里,我主要来做一次查漏补缺,以及分享一些阅读源码的一些心得。
首先,简单的来看看绘制流程经常用到的方法:
注意:为了方便阅读我故意把一些有参数的方法参数也去掉了。
View
measure()、onmeasure()
从上图可以看出,View的requestFitSystemWindows, requestLayout, invalidateChildInParent时候,都会调用measure方法。
而onMeasure方法一般是需要自己去定义高宽规则的时候重写它。
再者,measure的三种mode要分清楚:
MeasureSpec.UNSPECIFIED://未指定 (一般不用)
MeasureSpec.AT_MOST://至多 wrap_content
MeasureSpec.EXACTLY://精确 指定高度、宽度
layout()、onLayout()
1. 自定义一个view时,建议重写onLayout,以设定它的位置。
在外部调用时,调用layout(),触发设定位置。
2. 自定义一个viewGroup时,必须且只能重写onLayout。
需要在设定子view的位置:调用subview.layout(); 触发
draw()、ondraw()、dispatchdraw()
draw过程会调用onDraw(Canvas canvas)方法,然后就是dispatchDraw(Canvas canvas)方法, dispatchDraw()主要是分发给子组件进行绘制,我们通常定制组件的时候重写的是onDraw()方法。值得注意的是ViewGroup容器组件的绘制,当它没有背景时直接调用的是dispatchDraw()方法, 而绕过了draw()方法,当它有背景的时候就调用draw()方法,而draw()方法里包含了dispatchDraw()方法的调用。因此要在ViewGroup上绘制东西的时候往往重写的是dispatchDraw()方法而不是onDraw()方法,或者自定制一个Drawable,重写它的draw(Canvas c)和getIntrinsicWidth(),
invalidate()、postInvalidate()
invalidate()是用来刷新View的,必须是在UI线程中进行工作。比如在修改某个view的显示时,调用invalidate()才能看到重新绘制的界面。invalidate()的调用是把之前的旧的view从主UI线程队列中pop掉。 一个Android 程序默认情况下也只有一个进程,但一个进程下却可以有许多个线程。
requestLayout()
当一个View需要parent view重新调用他的onMeasure onLayout来对重新设置自己位置时,特别是当你修改了view的layout parameter,它的值还没能应用到view上,调用这个方法就可以了,但是要注意parent view的onLayout是怎么实现的。