1 super.onDraw() 前 or 后?
一般的自定义绘制都是直接继承 View 类,然后在 onDraw() 中重写它的方法,实现自定义它的绘制。然而,除了继承 View 类,自定义绘制更为常见的情况是,根据自己的需求,判断出你绘制的内容需要盖住控件原有的内容还是需要被控件原有的内容盖住,从而确定你的绘制代码是应该写在 super.onDraw() 的上面还是下面。
把绘制代码写在 super.onDraw() 的下面,由于绘制代码会在原有内容绘制结束之后才执行,所以绘制内容就会盖住控件原来的内容。而如果把绘制代码写在 super.onDraw() 的上面,由于绘制代码会执行在原有内容的绘制之前,所以绘制的内容会被控件的原内容盖住。
2 dispatchDraw():绘制子 View 的方法
讲了那么久的 onDraw() ,其实 onDraw() 只是负责自身主体内容绘制的。而有的时候,你想要的遮盖关系无法通过 onDraw() 来实现,而是需要通过别的绘制方法。
在绘制过程中,每个 View 和 ViewGroup 都会先调用 onDraw() 方法来绘制主体,再调用 dispatchDraw() 方法来绘制子 View。
这里提醒一下:虽然 View 和 ViewGroup 都有 dispatchDraw() 方法,不过由于 View 是没有子 View 的,所以一般来说 dispatchDraw() 这个方法只对 ViewGroup (以及它的子类)有意义。
这里演示一下(xml文件忘记贴图了,其实也就是设置了一个imageView):
同理,把绘制代码写在 super.dispatchDraw() 的上面,这段绘制就会在 onDraw() 之后、 super.dispatchDraw() 之前发生,也就是绘制内容会出现在主体内容和子 View 之间,然后这里就不会有红色斑点了。
3 绘制过程简述
绘制过程中最典型的两个部分是上面讲到的主体和子 View,但它们并不是绘制过程的全部。除此之外,绘制过程还包含一些其他内容的绘制。具体来讲,一个完整的绘制过程会依次绘制以下几个内容:
- 背景
- 主体( onDraw() )
- 子View( dispatchDraw() )
- 滑动边缘渐变和滑动条
- 前景
一般来说,一个 View(或 ViewGroup)的绘制不会这几项全都包含,但必然逃不出这几项,并且一定会严格遵守这个顺序。
例如通常一个 LinearLayout 只有背景和子 View,那么它会先绘制背景再绘制子 View;一个 ImageView 有主体,有可能会再加上一层半透明的前景作为遮罩,那么它的前景也会在主体之后进行绘制。需要注意,前景的支持是在 Android 6.0(也就是 API 23)才加入的;之前其实也有,不过只支持 FrameLayout ,而直到 6.0 才把这个支持放进了 View 类里。
3.1 背景
背景的绘制发生在一个叫 drawBackground() 的方法里,但这个方法是 private 的,不能重写,你如果要设置背景,只能用自带的 API 去设置(xml 布局文件的 android:background 属性以及 Java 代码的 View.setBackgroundXxx 方法),而不能自定义绘制;这里的主体和子View已经讲过了,那么我就跳过直接讲吧。
3.2 滑动边缘渐变和滑动条以及前景
这两部分被合在一起放在了 onDrawForeground() 方法里,这个方法是可以重写的。
滑动边缘渐变和滑动条可以通过 xml 的 android:scrollbarXXX
系列属性或 Java 代码的 View.setXXXScrollbarXXX()
系列方法来设置;前景可以通过 xml 的 android:foreground
属性或 Java 代码的 View.setForeground()
方法来设置。而重写 onDrawForeground()
方法,并在它的 super.onDrawForeground()
方法的上面或下面插入绘制代码,则可以控制绘制内容和滑动边缘渐变、滑动条以及前景的遮盖关系。
好啦,今天就先讲到这里,希望大家都能越来越优秀!