Android中的Canvas:什么是Canvas,Canvas类和draw方法

作者: IT Sectr 发布日期: 2026-07-21 阅读时间: 7 分钟

Android中的Canvas — 是来自android.graphics包的一个类,提供了在View上进行2D绘制的API。Canvas就像一块画布,开发者可以在上面使用各种draw*方法绘制几何图形、文本、图像和路径。根据Android Developers Documentation(2025),Canvas是Android中自定义绘制的主要工具,并与Paint类一起使用来设置样式和颜色。每次调用onDraw()都会获得一个已与视图的位图缓冲区关联的Canvas实例。

要点

  • Canvas — Android中用于2D绘制的类,提供drawRect、drawCircle、drawText、drawBitmap等方法
  • Paint — 定义绘制样式的类:颜色、粗细、填充、效果(阴影、渐变)
  • onDraw(Canvas) — View的方法,所有绘制通过接收到的Canvas进行
  • Canvas.save()和restore() — 管理变换栈,用于坐标系统的嵌套变换
  • Path — 用于构建任意轮廓和曲线的类,传递给drawPath(Path, Paint)

Android中的Canvas是什么

Canvas — 是Android中核心的2D图形类,提供在平面上绘制的方法。每个Canvas都与一个Bitmap(位图)相关联——这是一块内存区域,绘制操作的结果会写入其中。当View被重新绘制时,系统会将一个已与该View的缓冲区关联的Canvas传递给onDraw()。

Canvas在坐标系统中工作,其中(0,0)是左上角,X轴向右,Y轴向下。所有绘制操作都使用Canvas的当前状态,包括变换矩阵、裁剪区域(clip)和Paint样式。根据Android Documentation(2025),Canvas API包括30多种draw*方法,用于不同类型的图形基元。

Canvas与硬件加速

自Android 3.0(API 11)起,Canvas通过DisplayList支持硬件加速。在加速模式下,Canvas操作被记录到DisplayList中,然后在GPU上执行。这带来了显著的性能提升,但也施加了限制:某些Canvas操作(带非Path裁剪的clipPath)在加速模式下不被支持。

Canvas的主要绘制方法

Canvas提供了一套用于绘制基本基元的方法。每个方法接受几何参数和一个Paint对象,该对象定义了视觉样式。

方法用途示例
drawRect()绘制矩形canvas.drawRect(10f, 10f, 100f, 100f, paint)
drawCircle()绘制圆形canvas.drawCircle(50f, 50f, 30f, paint)
drawLine()绘制直线canvas.drawLine(0f, 0f, 100f, 100f, paint)
drawText()绘制文本canvas.drawText("Hello", 10f, 20f, paint)
drawBitmap()绘制图像canvas.drawBitmap(bitmap, 0f, 0f, paint)
drawPath()绘制任意路径canvas.drawPath(path, paint)
drawOval()绘制椭圆canvas.drawOval(rectF, paint)
drawArc()绘制弧线或扇形canvas.drawArc(rectF, 0f, 90f, true, paint)

每个方法可以按任意顺序调用——Canvas按照调用顺序绘制,将新元素叠加在之前的元素之上。对于复杂构图,使用多层绘制技术,通过保存和恢复Canvas状态来实现。

Paint类:样式和效果的配置

Paint — 是定义Canvas如何绘制的类。没有Paint,Canvas的方法毫无用处——它设置颜色、线条粗细、填充样式、文本大小、效果(阴影、渐变、模糊)和混合模式。

Paint具有关键属性:color(颜色)、style(FILL、STROKE或FILL_AND_STROKE)、strokeWidth(线条粗细)、textSize(文本大小)、isAntiAlias(抗锯齿)、shader(渐变或图像)。创建Paint对象是一项昂贵的操作,因此应该创建一次并重复使用。

kotlin
private val fillPaint = Paint().apply {
    color = Color.parseColor("#FF6200EE")
    style = Paint.Style.FILL
    isAntiAlias = true
}

private val strokePaint = Paint().apply {
    color = Color.WHITE
    style = Paint.Style.STROKE
    strokeWidth = 4f
    isAntiAlias = true
}

override fun onDraw(canvas: Canvas) {
    super.onDraw(canvas)
    canvas.drawCircle(100f, 100f, 80f, fillPaint)
    canvas.drawCircle(100f, 100f, 80f, strokePaint)
}

在这个例子中使用了两个Paint:一个用于用紫色填充圆形,另一个用于白色描边。将fill和stroke分开是标准做法,可以在不重新创建对象的情况下灵活管理外观。

Shader和渐变

对于渐变填充,Paint接受一个Shader(着色器)——LinearGradient、RadialGradient、SweepGradient子类。Shader通过setShader()方法附加到Paint,并定义颜色在绘制区域中如何变化。

Canvas的变换:translate、rotate、scale

Canvas支持坐标系统的矩阵变换:translate()(平移)、rotate()(旋转)、scale()(缩放)和skew()(倾斜)。变换会累积:每次后续调用都应用在之前的变换之上。

变换栈的管理通过save()restore()进行。save()将Canvas的当前状态(矩阵、裁剪区域)保存到栈中,restore()恢复最后保存的状态。在save()和restore()之间可以有嵌套的变换。

kotlin
override fun onDraw(canvas: Canvas) {
    super.onDraw(canvas)

    canvas.save() // 保存初始状态
    canvas.translate(100f, 100f) // 平移原点

    // 第一个正方形
    canvas.drawRect(0f, 0f, 50f, 50f, fillPaint)

    canvas.save() // 平移后保存
    canvas.rotate(45f, 25f, 25f) // 围绕正方形中心旋转

    // 旋转的正方形
    canvas.drawRect(0f, 0f, 50f, 50f, strokePaint)
    canvas.restore() // 旋转后恢复

    canvas.restore() // 恢复初始状态
}

始终在开始变换之前使用save(),在完成变换之后使用restore()。不平衡的save/restore会导致矩阵累积以及后续元素的错误绘制。

使用Bitmap和Path的Canvas复杂图形

对于复杂图形,Canvas与BitmapPath结合使用。Bitmap用于加载图像和缓存渲染好的帧。Path用于构建任意贝塞尔曲线、多边形和复杂轮廓。

离屏Bitmap缓冲区

离屏渲染技术:创建一个Bitmap,通过单独的Canvas在其上绘制复杂场景,然后通过drawBitmap()在onDraw()中显示准备好的Bitmap。这允许更新复杂图形而无需完全重绘每一帧。

kotlin
private var offscreenBitmap: Bitmap? = null
private var offscreenCanvas: Canvas? = null

fun buildOffscreenScene(width: Int, height: Int) {
    offscreenBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
    offscreenCanvas = Canvas(offscreenBitmap)

    // 渲染场景一次
    offscreenCanvas!!.drawColor(Color.WHITE)
    drawGrid(offscreenCanvas!!)
    drawComplexShape(offscreenCanvas!!)
}

override fun onDraw(canvas: Canvas) {
    super.onDraw(canvas)
    offscreenBitmap?.let { canvas.drawBitmap(it, 0f, 0f, null) }
}

Path用于构建复杂轮廓:贝塞尔曲线(quadTo、cubicTo)、弧线(arcTo)、直线(lineTo)。Path可以是闭合的(close())用于填充图形,或开放的用于线条。主要的drawPath()方法将Path和Paint结合起来进行最终绘制。

Canvas绘制的优化

Canvas绘制的性能直接影响应用程序的帧率。主要问题:onDraw()中的过多分配、频繁的invalidate()以及对硬件加速的非最优使用。

在onDraw()之外创建对象

Paint、Path、Rect、RectF和其他对象应在初始化器或setup()方法中一次性创建。onDraw()中的每次分配都会增加垃圾回收器的负担,导致微卡顿。例外——staticcompanion object用于不可变对象。

使用clipRect()限制绘制区域

如果View的很大部分隐藏在可见区域之外(例如在ScrollView中),使用canvas.clipRect()来限制绘制。硬件加速在这种情况下会进一步优化不可见像素的裁剪。

kotlin
override fun onDraw(canvas: Canvas) {
    super.onDraw(canvas)

    // 裁剪到可见区域
    canvas.save()
    canvas.clipRect(scrollX, scrollY, scrollX + width, scrollY + height)

    // 渲染大场景
    renderScene(canvas)
    canvas.restore()
}

对于具有不常更新的复杂Canvas图形的View,使用View.setLayerType()配合LAYER_TYPE_HARDWARE。对于频繁更新的动画,使用LAYER_TYPE_NONE以避免昂贵的硬件层更新。通过GPU Profile RenderingAndroid Studio Profiler进行性能分析。

常见问题

可以在没有View的情况下创建Canvas吗?

是的,可以创建Canvas(Bitmap)——接受Bitmap的构造函数。这用于离屏渲染:在位图上绘制,然后通过drawBitmap()显示。Canvas也可以从Surface(用于SurfaceView)或通过Picture创建。

Canvas和SurfaceView有什么区别?

Canvas — 是在UI线程中在View上绘制的API。SurfaceView — 是一个独立的Surface,可以通过自己的Canvas(lockCanvas() / unlockCanvasAndPost())从后台线程更新。SurfaceView更适用于游戏和视频。

为什么Canvas在变换后不绘制?

最常见的原因是save()restore()不匹配。如果变换(translate、rotate)后没有调用restore(),Canvas会保持在变换后的坐标系统中,后续元素会偏移绘制。检查save()/restore()对。

如何在Canvas中心绘制文本?

使用Paint.setTextAlign(Paint.Align.CENTER)进行水平对齐,使用Paint.getTextBounds()Paint.descent() / ascent()进行垂直对齐。居中:canvas.drawText(text, centerX, centerY - (ascent + descent) / 2, paint)。

Canvas支持动画吗?

Canvas本身不制作动画——它只绘制当前状态。动画通过连续调用invalidate()(或postInvalidateOnAnimation())实现,每次在onDraw()中绘制新帧。为获得流畅的动画,使用ValueAnimatorChoreographer与Vsync同步。

总结

  • Canvas — Android中核心的2D绘制类,提供draw*方法用于绘制基元、文本和图像
  • Paint — 定义视觉样式(颜色、粗细、填充、着色器);创建一次并重复使用
  • save() / restore() — 变换栈管理;每个变换必须在save/restore对中
  • Path — 构建任意轮廓和贝塞尔曲线,用于复杂矢量图形
  • 离屏Bitmap — 通过Canvas(Bitmap)缓存复杂场景的技术,减少onDraw()的负载
  • 硬件加速 — 自Android 3.0起的Canvas GPU加速,对某些操作施加限制(clipPath)
  • 性能分析 — GPU Profile Rendering和Android Studio Profiler是查找onDraw()瓶颈的必要工具

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读