Canvas en Android: qué es, clase Canvas y métodos draw

Autor: IT Sectr Publicado: 2026-07-21 Tiempo de lectura: 7 min

Canvas en Android — es una clase del paquete android.graphics que proporciona una API para renderizado 2D en View. Canvas funciona como un lienzo en el que el desarrollador dibuja formas geométricas, texto, imágenes y trazados mediante varios métodos draw*. Según la Documentación para Desarrolladores de Android (2025), Canvas es la herramienta principal para el renderizado personalizado en Android y se utiliza junto con la clase Paint para definir estilos y colores. Cada llamada a onDraw() recibe una instancia de Canvas ya preparada, asociada con el búfer de mapa de bits de la vista.

Puntos Clave

  • Canvas — una clase para renderizado 2D en Android, proporciona métodos drawRect, drawCircle, drawText, drawBitmap y otros
  • Paint — una clase que define el estilo de renderizado: color, grosor, relleno, efectos (sombra, degradado)
  • onDraw(Canvas) — el método de View donde ocurre todo el renderizado a través del Canvas recibido
  • Canvas.save() y restore() — gestión de la pila de transformaciones para transformaciones anidadas del sistema de coordenadas
  • Path — una clase para construir contornos y curvas arbitrarias, se pasa a drawPath(Path, Paint)

Qué es Canvas en Android

Canvas — es la clase central de gráficos 2D en Android que proporciona métodos para dibujar en un plano. Cada Canvas está vinculado a un Bitmap — un área de memoria donde se registran los resultados de las operaciones de dibujo. Cuando una View se redibuja, el sistema pasa a onDraw() un Canvas ya asociado con el búfer de esa View.

Canvas funciona en un sistema de coordenadas donde (0,0) es la esquina superior izquierda, el eje X va hacia la derecha, el eje Y hacia abajo. Todas las operaciones de dibujo usan el estado actual de Canvas, incluyendo la matriz de transformación, el área de recorte y el estilo de Paint. Según la Documentación de Android (2025), la API de Canvas incluye más de 30 métodos draw* para diferentes tipos de primitivas gráficas.

Canvas y Aceleración por Hardware

Desde Android 3.0 (API 11) Canvas admite aceleración por hardware a través de DisplayList. En modo acelerado, las operaciones de Canvas se registran en una DisplayList y luego se ejecutan en la GPU. Esto proporciona un aumento significativo de rendimiento pero impone limitaciones: algunas operaciones de Canvas (clipPath con clip no Path) no son compatibles en modo acelerado.

Métodos básicos de Canvas para renderizado

Canvas proporciona un conjunto de métodos para dibujar primitivas básicas. Cada método acepta parámetros geométricos y un objeto Paint que define el estilo visual.

MétodoPropósitoEjemplo
drawRect()Dibuja un rectángulocanvas.drawRect(10f, 10f, 100f, 100f, paint)
drawCircle()Dibuja un círculocanvas.drawCircle(50f, 50f, 30f, paint)
drawLine()Dibuja una líneacanvas.drawLine(0f, 0f, 100f, 100f, paint)
drawText()Dibuja textocanvas.drawText(“Hello”, 10f, 20f, paint)
drawBitmap()Dibuja una imagencanvas.drawBitmap(bitmap, 0f, 0f, paint)
drawPath()Dibuja un trazado arbitrariocanvas.drawPath(path, paint)
drawOval()Dibuja un óvalocanvas.drawOval(rectF, paint)
drawArc()Dibuja un arco o sectorcanvas.drawArc(rectF, 0f, 90f, true, paint)

Cada método puede llamarse en cualquier orden — Canvas dibuja en el orden de las llamadas, superponiendo nuevos elementos sobre los anteriores. Para composiciones complejas, se utiliza la técnica de renderizado multicapa con guardado y restauración del estado de Canvas.

Clase Paint: configuración de estilos y efectos

Paint — es la clase que define cómo Canvas dibujará. Sin Paint, los métodos de Canvas son inútiles — establece color, grosor de línea, estilo de relleno, tamaño de texto, efectos (sombra, degradado, desenfoque) y modo de mezcla.

Paint tiene propiedades clave: color, style (FILL, STROKE o FILL_AND_STROKE), strokeWidth, textSize, isAntiAlias, shader (degradado o imagen). Crear un objeto Paint es una operación costosa, por lo que debe crearse una vez y reutilizarse.

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)
}

En este ejemplo, se usan dos objetos Paint: uno para rellenar el círculo con color púrpura, el segundo para el trazo blanco. Separar relleno y trazo es una práctica estándar que permite un control flexible de la apariencia sin recrear objetos.

Shader y degradados

Para rellenos degradados, Paint acepta un Shader — subclases LinearGradient, RadialGradient, SweepGradient. Shader se vincula a Paint mediante el método setShader() y define cómo cambia el color en el área de dibujo.

Transformaciones de Canvas: translate, rotate, scale

Canvas admite transformaciones matriciales del sistema de coordenadas: translate(), rotate(), scale() y skew(). Las transformaciones se acumulan: cada llamada posterior se aplica sobre las anteriores.

La gestión de la pila de transformaciones se realiza mediante save() y restore(). save() guarda el estado actual de Canvas (matriz, recorte) en la pila, restore() restaura el último estado guardado. Entre save() y restore() se pueden aplicar transformaciones anidadas.

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

    canvas.save() // Save initial state
    canvas.translate(100f, 100f) // Translate origin

    // First square
    canvas.drawRect(0f, 0f, 50f, 50f, fillPaint)

    canvas.save() // Save after translate
    canvas.rotate(45f, 25f, 25f) // Rotate around square center

    // Rotated square
    canvas.drawRect(0f, 0f, 50f, 50f, strokePaint)
    canvas.restore() // Restore after rotate

    canvas.restore() // Restore initial state
}

Use siempre save() antes de comenzar las transformaciones y restore() después de completarlas. Los pares save/restore desbalanceados provocan acumulación de matriz y renderizado incorrecto de elementos posteriores.

Canvas con Bitmap y Path para gráficos complejos

Para gráficos complejos, Canvas se combina con Bitmap y Path. Bitmap se usa para cargar imágenes y almacenar en caché fotogramas renderizados. Path se usa para construir curvas Bezier arbitrarias, polígonos y contornos complejos.

Búfer Bitmap fuera de pantalla

La técnica de renderizado fuera de pantalla: se crea un Bitmap, se dibuja una escena compleja en él a través de un Canvas separado, y luego el Bitmap terminado se muestra en onDraw() mediante drawBitmap(). Esto permite actualizar gráficos complejos sin redibujar completamente en cada fotograma.

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)

    // Render scene once
    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 se usa para construir contornos complejos: curvas Bezier (quadTo, cubicTo), arcos (arcTo), líneas (lineTo). Path puede estar cerrado (close()) para formas rellenas o abierto para líneas. El método principal drawPath() combina Path y Paint para el renderizado final.

Optimización del renderizado de Canvas

El rendimiento del renderizado de Canvas afecta directamente los FPS de la aplicación. Los principales problemas: asignaciones excesivas en onDraw(), llamadas frecuentes a invalidate() y uso subóptimo de la Aceleración por Hardware.

Cree objetos fuera de onDraw()

Paint, Path, Rect, RectF y otros objetos deben crearse una vez en el inicializador o método setup(). Cada asignación en onDraw() genera presión en el recolector de basura, lo que provoca microrretrasos. La excepción son static o companion object para objetos inmutables.

Use clipRect() para limitar el área

Si la mayor parte de la View está oculta fuera del área visible (por ejemplo, en ScrollView), use canvas.clipRect() para limitar el renderizado. La Aceleración por Hardware en este caso optimiza adicionalmente el recorte de píxeles invisibles.

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

    // Clip to visible area
    canvas.save()
    canvas.clipRect(scrollX, scrollY, scrollX + width, scrollY + height)

    // Render large scene
    renderScene(canvas)
    canvas.restore()
}

Use View.setLayerType() con LAYER_TYPE_HARDWARE para Views con gráficos Canvas complejos que se actualizan con poca frecuencia. Para animaciones actualizadas con frecuencia, por el contrario, use LAYER_TYPE_NONE para evitar costosas actualizaciones de la capa de hardware. Perfile usando GPU Profile Rendering y Android Studio Profiler.

Preguntas Frecuentes

¿Se puede crear un Canvas sin una View?

Sí, se puede crear Canvas(Bitmap) — un constructor que acepta un Bitmap. Esto se usa para renderizado fuera de pantalla: dibuja en un Bitmap y luego lo muestra mediante drawBitmap(). Canvas también puede crearse desde Surface (para SurfaceView) o mediante Picture.

¿En qué se diferencia Canvas de SurfaceView?

Canvas — es una API para dibujar en View en el hilo de UI. SurfaceView — es un Surface separado que puede actualizarse desde un hilo en segundo plano a través de su propio Canvas (lockCanvas() / unlockCanvasAndPost()). SurfaceView es preferible para juegos y video.

¿Por qué Canvas no dibuja después de una transformación?

La razón más común son los pares desbalanceados de save() y restore(). Si después de una transformación (translate, rotate) no se llama a restore(), Canvas permanece en un sistema de coordenadas modificado y los elementos posteriores se dibujan con desplazamiento. Verifique sus pares save()/restore().

¿Cómo dibujar texto centrado en Canvas?

Use Paint.setTextAlign(Paint.Align.CENTER) para centrado horizontal y Paint.getTextBounds() o Paint.descent() / ascent() para vertical. Centrado: canvas.drawText(texto, centerX, centerY - (ascent + descent) / 2, paint).

¿Canvas admite animación?

Canvas por sí mismo no anima — solo dibuja el estado actual. La animación se logra mediante llamadas secuenciales a invalidate() (o postInvalidateOnAnimation()), dibujando cada vez un nuevo fotograma en onDraw(). Para animaciones suaves use ValueAnimator o Choreographer para sincronizar con Vsync.

Resumen

  • Canvas — la clase central de renderizado 2D en Android, proporciona métodos draw* para dibujar primitivas, texto e imágenes
  • Paint — define el estilo visual (color, grosor, relleno, shaders); se crea una vez y se reutiliza
  • save() / restore() — gestión de la pila de transformaciones; cada transformación debe estar en un par save/restore
  • Path — construcción de contornos arbitrarios y curvas Bezier para gráficos vectoriales complejos
  • Offscreen Bitmap — técnica de almacenamiento en caché de escenas complejas mediante Canvas(Bitmap) para reducir la carga en onDraw()
  • Aceleración por Hardware — aceleración GPU de Canvas desde Android 3.0, impone limitaciones en algunas operaciones (clipPath)
  • Perfilado — GPU Profile Rendering y Android Studio Profiler son obligatorios para encontrar cuellos de botella en onDraw()

Desarrollaremos una aplicación móvil llave en mano

IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.

Discutir el proyecto

Lea también