-
一、简介
Android 的绘制是按顺序绘制的,先绘制的内容会被后绘制的内容所覆盖,并且Android 里面关于绘制的方法是比较多的,onDraw()、dispatchDraw()、onDrawForeground()、draw()方法在绘制顺序里面也起到关键的作用,本文主要在于介绍这些方法在绘制顺序中起到的一些作用
-
二、关键的方法
-
onDraw()
1、 onDraw() 方法介绍:
是绘制过程中最为常用的重写方法,通过重写onDraw()方法可以绘制出很多不同形状的图形,文字,例子如下
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
var drawable = drawable
if (BuildConfig.DEBUG && drawable!=null){
canvas?.save()
canvas?.concat(imageMatrix)
var bounds = drawable.bounds
canvas?.drawText(resources.getString(R.string.image_size,bounds.width(),bounds.height()),20f,40f,paint)
canvas?.restore()
}
}
2、我们draw的一些代码是要放在super.onDraw()前还是后?
放在 super.onDraw()之前 :
由于绘制的代码都在原控件的绘制之前调用,所以绘制的内容会被原控件的绘制内容所遮挡。
这里例子展示了一个带背景颜色的TextView,由于在super.onDraw(),就已经绘制了颜色,后面写的一些文字的内容都会有带背景颜色,eg:
override fun onDraw(canvas: Canvas?) {
var layout = layout
bounds.left = layout.getLineLeft(0)
bounds.right = layout.getLineRight(0)
bounds.top = layout.getLineTop(0).toFloat()
bounds.bottom = layout.getLineBottom(0).toFloat()
canvas?.drawRect(bounds,paint)
super.onDraw(canvas)
}
放在 super.onDraw()之后 :
由于绘制代码会在原有内容绘制结束之后才执行,所以绘制内容就会覆盖控件原来的内容。
这里例子展示了一个在debug情况下会在ImageView控件中输出图片尺寸的一个demo,eg:
//draw() 是绘制过程的总调度方法。一个 View 的整个绘制过程都发生在 draw() 方法里。前面讲到的背景、主体、子 View 、滑动相关以及前景的绘制,它们其实都是在 draw() 方法里的。
override fun draw(canvas: Canvas?) {
super.draw(canvas)
// 在 super.draw() 的下方插入绘制代码,让绘制内容盖住其他所有
paint.color = Color.parseColor("#f44336")
canvas?.drawRect(0f, 40f, 200f, 120f, paint)
paint.color = Color.WHITE
canvas?.drawText("New", 20f, 100f, paint)
}
-
dispatchDraw()
1、dispatchDraw() 方法介绍:
在子view绘制完之后才调用的方法,可以通过重写这个方法让你绘制的内容覆盖在子view上面,若没有通过这个方法,父控件绘制的内容会被子控件所覆盖
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
// 在 super.onDraw() 的下方插入绘制代码,绘制一些点
drawPoint(canvas = canvas)
}
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<com.zy.draworder.AfterDispatchDrawView
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="200dp"
android:layout_height="200dp"
android:src="@drawable/logo"
/>
</com.zy.draworder.AfterDispatchDrawView>
</FrameLayout>
2、我们draw的一些代码是要放在super.dispatchDraw()前还是后?
放在 super.dispatchDraw()之前 :
把绘制代码写在 super.dispatchDraw() 的上面,这段绘制就会在子view的 onDraw() 之后、 super.dispatchDraw() 之前发生,也就是绘制内容会出现在主体内容和子 View 之间。
//把绘制代码写在 super.dispatchDraw() 的上面,这段绘制就会在 onDraw() 之后、 super.dispatchDraw() 之前发生,也就是绘制内容会出现在主体内容和子 View 之间。
override fun dispatchDraw(canvas: Canvas?) {
drawPoint(canvas = canvas)
super.dispatchDraw(canvas)
}
fun drawPoint(canvas: Canvas?){
paint.color = Color.GREEN
canvas?.drawCircle(width * 0.1f,height * 0.1f,55f,paint)
canvas?.drawCircle(width * 0.2f,height * 0.2f,15f,paint)
canvas?.drawCircle(width * 0.3f,height * 0.3f,15f,paint)
canvas?.drawCircle(width * 0.4f,height * 0.4f,15f,paint)
canvas?.drawCircle(width * 0.5f,height * 0.5f,15f,paint)
paint.color = Color.RED
canvas?.drawCircle(width * 0.9f,height * 0.1f,55f,paint)
canvas?.drawCircle(width * 0.8f,height * 0.2f,15f,paint)
canvas?.drawCircle(width * 0.7f,height * 0.3f,15f,paint)
canvas?.drawCircle(width * 0.6f,height * 0.4f,15f,paint)
paint.shader = RadialGradient(width * 0.1f,height * 0.1f,55f,Color.parseColor("#E91E63"),
Color.parseColor("#2196F3"), Shader.TileMode.CLAMP)
canvas?.drawCircle(width * 0.5f,height * 0.5f,55f,paint)
}
放在 super.dispatchDraw()之后 :
把绘制代码写在 super.dispatchDraw() 的下面,这段绘制就会在子view的 onDraw() 之后、 super.dispatchDraw() 之后发生,也就是绘制内容会出现在主体内容和子 View 内容的后面,从而让绘制内容盖住子 View 了。
//只要重写 dispatchDraw(),并在 super.dispatchDraw() 的下面写上你的绘制代码,这段绘制代码就会发生在子 View 的绘制之后,从而让绘制内容盖住子 View 了。
override fun dispatchDraw(canvas: Canvas?) {
super.dispatchDraw(canvas)
drawPoint(canvas = canvas)
}
fun drawPoint(canvas: Canvas?){
paint.color = Color.GREEN
canvas?.drawCircle(width * 0.1f,height * 0.1f,55f,paint)
canvas?.drawCircle(width * 0.2f,height * 0.2f,15f,paint)
canvas?.drawCircle(width * 0.3f,height * 0.3f,15f,paint)
canvas?.drawCircle(width * 0.4f,height * 0.4f,15f,paint)
canvas?.drawCircle(width * 0.5f,height * 0.5f,15f,paint)
paint.color = Color.RED
canvas?.drawCircle(width * 0.9f,height * 0.1f,55f,paint)
canvas?.drawCircle(width * 0.8f,height * 0.2f,15f,paint)
canvas?.drawCircle(width * 0.7f,height * 0.3f,15f,paint)
canvas?.drawCircle(width * 0.6f,height * 0.4f,15f,paint)
paint.shader = RadialGradient(width * 0.1f,height * 0.1f,55f,Color.parseColor("#E91E63"),
Color.parseColor("#2196F3"), Shader.TileMode.CLAMP)
canvas?.drawCircle(width * 0.5f,height * 0.5f,55f,paint)
}
-
onDrawForeground()
1、 onDrawForeground() 方法介绍:
这个方法是 API 23 才引入的,所以在重写这个方法的时候要确认你的 minSdk 达到了 23,不然低版本的手机装上你的软件会没有效果。
在 onDrawForeground() 中,会依次绘制滑动边缘渐变、滑动条和前景。所以如果你重写 onDrawForeground()
2、我们draw的一些代码是要放在super.onDrawForeground()前还是后?
放在 super.onDrawForeground()之前 :
如果你把绘制代码写在了 super.onDrawForeground() 的上面,绘制内容就会在 dispatchDraw() 和 super.onDrawForeground() 之间执行,那么绘制内容会盖住子 View,但被滑动边缘渐变、滑动条以及前景盖住:
override fun onDrawForeground(canvas: Canvas?) {
paint.color = Color.parseColor("#f44336")
canvas?.drawRect(0f, 40f, 200f, 120f, paint)
paint.color = Color.WHITE
canvas?.drawText("Foreground", 20f, 100f, paint)
super.onDrawForeground(canvas)
}
放在 super.onDrawForeground()之后 :
如果你把绘制代码写在了 super.onDrawForeground() 的下面,绘制代码会在滑动边缘渐变、滑动条和前景之后被执行,那么绘制内容将会盖住滑动边缘渐变、滑动条和前景。
override fun onDrawForeground(canvas: Canvas?) {
super.onDrawForeground(canvas)
//在super.onDrawForeground() 的下方插入绘制代码,让绘制内容盖住前景
paint.color = Color.parseColor("#f44336")
canvas?.drawRect(0f, 40f, 200f, 120f, paint)
paint.color = Color.WHITE
canvas?.drawText("Foreground", 20f, 100f, paint)
}
-
draw()
1、 draw() 方法介绍:
draw() 是绘制过程的总调度方法。一个 View 的整个绘制过程都发生在 draw() 方法里。前面讲到的背景、主体、子 View 、滑动相关以及前景的绘制,它们其实都是在 draw() 方法里的。
public void draw(Canvas canvas) {
...
drawBackground(Canvas); // 绘制背景(不能重写)
onDraw(Canvas); // 绘制主体
dispatchDraw(Canvas); // 绘制子 View
onDrawForeground(Canvas); // 绘制滑动相关和前景
...
}
2、我们draw的一些代码是要放在super.draw()前还是后?
放在 super.draw()之前 :
由于 draw() 是总调度方法,所以如果把绘制代码写在 super.draw() 的上面,那么这段代码会在其他所有绘制之前被执行,所以这部分绘制内容会被其他所有的内容盖住,包括背景。是的,背景也会盖住它。
override fun draw(canvas: Canvas?) {
//于 draw() 是总调度方法,所以如果把绘制代码写在 super.draw() 的上面,那么这段代码会在其他所有绘制之前被执行,所以这部分绘制内容会被其他所有的内容盖住,包括背景。是的,背景也会盖住它。
canvas?.drawColor(Color.parseColor("#00BB6A")); // 涂上绿色
super.draw(canvas)
}
放在 super.draw()之后 :
由于 draw() 是总调度方法,所以如果把绘制代码写在 super.draw() 的下面,那么这段代码会在其他所有绘制完成之后再执行,也就是说,它的绘制内容会盖住其他的所有绘制内容。
它的效果和重写 onDrawForeground(),并把绘制代码写在 super.onDrawForeground() 下面时的效果是一样的:都会盖住其他的所有内容。
override fun draw(canvas: Canvas?) {
super.draw(canvas)
// 在 super.draw() 的下方插入绘制代码,让绘制内容盖住其他所有
paint.color = Color.parseColor("#f44336")
canvas?.drawRect(0f, 40f, 200f, 120f, paint)
paint.color = Color.WHITE
canvas?.drawText("New", 20f, 100f, paint)
}
-
总结: