onDraw() — qué es, mecanismo de llamada y sobrescritura del método

Autor: IT Sectr Publicado: 2026-07-22 Tiempo de lectura: 10 min

onDraw() es un método clave de la clase View en el SDK de Android responsable de dibujar el contenido de una vista. El sistema llama a onDraw en cada actualización de la apariencia de la View — durante el renderizado inicial, después de invalidate() o cuando cambian las dimensiones. Según la documentación de Android Developers (2026), sobrescribir onDraw usando la API de Canvas es la forma principal de crear gráficos personalizados en aplicaciones Android.

Puntos clave

  • onDraw(Canvas) — un método de View llamado por el sistema para dibujar contenido en el Canvas
  • invalidate() — una solicitud para redibujar la View, lo que provoca que se llame a onDraw en el siguiente ciclo de fotograma
  • Canvas API proporciona los métodos drawCircle, drawBitmap, drawText, drawPath y otros para dibujar
  • El sistema Android realiza el dibujo en el hilo de UI, por lo que onDraw debe ser ligero y rápido
  • Hardware acceleration acelera automáticamente el dibujo a través de la GPU, desde Android 3.0 (API 11)

¿Qué es onDraw()?

onDraw(Canvas canvas) es un método protected de la clase android.view.View, que el sistema Android llama cuando una View debe dibujarse. El desarrollador sobrescribe este método para implementar dibujo personalizado: formas, texto, imágenes, degradados, animaciones. El parámetro Canvas es una superficie gráfica sobre la que se realizan todas las operaciones de dibujo.

La firma del método es simple: override fun onDraw(canvas: Canvas). Dentro de este método no se deben crear nuevos objetos (para evitar la carga del GC), realizar operaciones largas o trabajar con la red. View también incluye el método onDraw en su ciclo de vida entre onLayout (cálculo de posiciones) y dispatchDraw (dibujo de elementos hijos).

Al heredar de View, sobrescribir onDraw es obligatorio para mostrar cualquier contenido — de lo contrario, la View estará vacía. Al heredar de subclases existentes (TextView, ImageView, Button), sobrescribir onDraw es opcional y se utiliza para agregar gráficos adicionales sobre el contenido estándar.

Ciclo de dibujo de View

Android realiza el dibujo de View en una secuencia estrictamente definida llamada desde el hilo principal (UI thread). El ciclo completo consta de tres fases: measure (medición de dimensiones), layout (cálculo de posiciones) y draw (renderizado). onDraw es parte de la fase draw y se llama después de que las dimensiones y la posición de la View ya se han determinado.

Fase draw: dispatchDraw y onDraw

draw(Canvas canvas) es un método público de View que llama a onDraw y dispatchDraw. El método draw no está diseñado para ser sobrescrito — en su lugar, se sobrescribe onDraw. Después de que onDraw se ejecuta para la View actual, se llama a dispatchDraw, que invoca recursivamente draw para todas las View hijas (si la View actual es un contenedor ViewGroup).

Cuándo el sistema llama a onDraw

onDraw se llama bajo las siguientes condiciones: 1) la View se vuelve visible por primera vez (después de setVisibility(VISIBLE)); 2) la aplicación llama a invalidate() en esta View o en su padre; 3) cambia el tamaño de la View (nuevo layout); 4) cambia una animación asociada a la View (a través de ValueAnimator u ObjectAnimator); 5) cambia el estado de la View (pressed, focused, enabled).

Aceleración por hardware

Desde Android 3.0 (API 11), la mayoría de las operaciones de Canvas se aceleran por hardware a través de la GPU. El desarrollador puede verificar si la aceleración está habilitada mediante canvas.isHardwareAccelerated(). Con la aceleración por hardware, algunas operaciones de Canvas no son compatibles (por ejemplo, clipPath con una región no rectangular) y pueden ignorarse o causar errores.

Canvas API para gráficos personalizados

Canvas proporciona un rico conjunto de métodos de dibujo. Todos los métodos funcionan en el sistema de coordenadas actual, que puede modificarse mediante transformaciones (translate, rotate, scale, skew). Cada método acepta un objeto Paint que define el color, el estilo, el grosor de línea, los efectos y el suavizado.

Método de CanvasPropósitoParámetros clave de Paint
drawCircleDibujar un círculocenterX, centerY, radius, paint
drawRectDibujar un rectánguloleft, top, right, bottom, paint
drawLineDibujar una líneastartX, startY, endX, endY, paint
drawPathDibujar un trazado arbitrariopath, paint
drawBitmapDibujar una imagen de mapa de bitsbitmap, srcRect, dstRect, paint
drawTextDibujar textotext, x, y, paint
drawArcDibujar un arco o sectorovalRect, startAngle, sweepAngle, useCenter, paint
drawOvalDibujar un óvaloovalRect, paint

El objeto Paint

Paint define el estilo de dibujo: color (setColor), grosor de línea (setStrokeWidth), estilo de relleno (setStyle — FILL, STROKE, FILL_AND_STROKE), suavizado (setAntiAlias), efectos (setShadowLayer, setMaskFilter) y degradados (setShader). Se recomienda crear y configurar Paint una vez en el constructor de la View, no dentro de onDraw, para evitar la creación de objetos en el bucle de dibujo.

Coordenadas y transformaciones

Canvas utiliza un sistema de coordenadas donde el eje X apunta a la derecha y el eje Y hacia abajo. El origen (0, 0) es la esquina superior izquierda de la View. Antes de llamar a los métodos de dibujo, el desarrollador puede cambiar el sistema de coordenadas mediante canvas.translate(dx, dy), canvas.rotate(degrees, px, py), canvas.scale(sx, sy, px, py). Todas las operaciones de dibujo posteriores se realizarán en el sistema transformado.

Rendimiento de onDraw

onDraw se ejecuta en el hilo principal de UI, y su duración afecta directamente la frecuencia de cuadros (fps). Para mantener 60 fps, el método onDraw debe completarse en menos de 16 milisegundos. Cualquier ralentización provoca caídas de fotogramas (jank), que visualmente se percibe como tartamudeo de animación o retraso en el desplazamiento.

Reglas de rendimiento para onDraw

Reglas clave para la optimización de onDraw incluyen: 1) no crear objetos dentro de onDraw — todos los objetos Paint, Path, Rect deben ser campos de clase; 2) no asignar memoria (new) — esto provoca recolección de basura (GC) y caídas de fotogramas; 3) almacenar en caché operaciones complejas en un Bitmap mediante Bitmap.createBitmap y dibujar el Bitmap preparado; 4) no realizar operaciones de E/S ni carga de recursos.

Uso de clipRect para optimización

clipRect limita el área de dibujo a un rectángulo especificado. Se utiliza para evitar que Canvas pierda tiempo dibujando píxeles fuera del área visible. Es especialmente efectivo al desplazar listas grandes o hacer zoom: establezca clipRect en el área visible (dirty rect), y Canvas solo dibujará lo que es visible.

Layer — guardar y restaurar

canvas.save() guarda el estado actual del Canvas (transformación y clip) en una pila. canvas.restore() restaura el último estado guardado. Esto es necesario para transformaciones temporales: guarda el estado, aplica translate/rotate, dibuja, luego restaura. save y restore son ligeros y no afectan el rendimiento cuando se usan de manera razonable (menos de 10–15 niveles de anidamiento).

invalidate y postInvalidate

invalidate() es un método de View que indica al sistema que el contenido de la View está desactualizado y necesita ser redibujado. Llamar a invalidate inicia un nuevo ciclo de draw en el que se llamará a onDraw. invalidate solo debe llamarse desde el hilo de UI (main thread). Para llamarlo desde un hilo en segundo plano, se usa postInvalidate.

postInvalidate() es una versión thread-safe de invalidate que envía una solicitud de redibujo a la cola del hilo principal a través de Handler. Se utiliza en tareas en segundo plano cuando la View necesita actualizarse después de completar la carga de datos o los cálculos. Se puede combinar con runOnUiThread para la sincronización con la UI.

Para redibujo parcial, use invalidate(Rect dirty) o invalidate(int l, int t, int r, int b). Esto le indica al sistema que solo el área especificada ha cambiado, no toda la View. Android optimiza el dibujo redibujando solo el dirty rect, lo que mejora significativamente el rendimiento en animaciones con actualizaciones parciales.

Ejemplos de código en Kotlin

Veamos ejemplos prácticos de sobrescritura de onDraw para crear View personalizadas en Android usando Kotlin.

View personalizada — Progreso circular

Este ejemplo crea un indicador de progreso circular con llenado animado. onDraw dibuja dos círculos: un círculo de fondo gris y un círculo de color cuyo arco está determinado por el progreso actual. Llamar a invalidate en setProgress asegura el redibujo cuando cambia el valor.

kotlin
class CircularProgressView
    @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null
) : View(context, attrs) {

    private val bgPaint = Paint().apply {
        style = Paint.Style.STROKE
        strokeWidth = 12f
        color = Color.parseColor("#E0E0E0")
        isAntiAlias = true
    }

    private val progressPaint = Paint().apply {
        style = Paint.Style.STROKE
        strokeWidth = 12f
        color = Color.BLUE
        isAntiAlias = true
        strokeCap = Paint.Cap.ROUND
    }

    var progress: Float = 0f
        set(value) {
            field = value.coerceIn(0f, 1f)
            invalidate()
        }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        val size = minOf(width, height) - 24f
        val oval = RectF(12f, 12f,
                          12f + size, 12f + size)

        canvas.drawArc(oval, 270f, 360f, false, bgPaint)
        canvas.drawArc(oval, 270f,
                        progress * 360f,
                        false, progressPaint)
    }
}

View personalizada — Fondo degradado con texto

Este ejemplo dibuja un rectángulo con degradado y texto encima. El degradado se crea mediante LinearGradient (Shader), y el texto mediante drawText. Ambos objetos Paint se crean una vez en el constructor, mientras que onDraw solo actualiza el tamaño del rectángulo según el ancho actual de la View.

kotlin
class GradientTextView(context: Context)
    : View(context) {

    private val shaderPaint = Paint().apply {
        isAntiAlias = true
    }

    private val textPaint = Paint().apply {
        color = Color.WHITE
        textSize = 48f
        isAntiAlias = true
        textAlign = Paint.Align.CENTER
    }

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

        val gradient = LinearGradient(
            0f, 0f, width.toFloat(), 0f,
            Color.parseColor("#FF6B35"),
            Color.parseColor("#FF0040"),
            Shader.TileMode.CLAMP
        )
        shaderPaint.shader = gradient
        canvas.drawRect(0f, 0f,
                        width.toFloat(),
                        height.toFloat(), shaderPaint)

        val xCenter = width / 2f
        val yCenter = height / 2f
        val yOffset = (textPaint.descent() +
                        textPaint.ascent()) / 2f

        canvas.drawText("Hello from Canvas",
                      xCenter, yCenter - yOffset,
                      textPaint)
    }
}

Reloj animado usando invalidate

Este ejemplo crea un reloj animado que actualiza el dibujo cada 1000 milisegundos. postDelayed dentro de onDraw llama a invalidate después de un segundo, creando un bucle infinito de redibujo. Las manecillas del reloj se dibujan mediante drawLine con diferentes longitudes y grosores.

kotlin
class AnalogClockView(context: Context)
    : View(context) {

    private val handPaint = Paint().apply {
        style = Paint.Style.STROKE
        isAntiAlias = true
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        val cx = width / 2f
        val cy = height / 2f
        val radius = minOf(cx, cy) - 20f

        val now = Calendar.getInstance()
        val hours = now.get(Calendar.HOUR) % 12
        val minutes = now.get(Calendar.MINUTE)
        val seconds = now.get(Calendar.SECOND)

        val secondAngle = Math.toRadians(
            seconds * 6f - 90f)
        handPaint.strokeWidth = 2f
        handPaint.color = Color.RED
        canvas.drawLine(cx, cy,
            cx + cos(secondAngle) * radius * 0.8f,
            cy + sin(secondAngle) * radius * 0.8f,
            handPaint)

        postDelayed({ invalidate() }, 1000)
    }
}

Preguntas frecuentes

¿Se puede llamar a onDraw directamente?

No, no se debe llamar a onDraw directamente. Para solicitar un redibujo, use invalidate() (desde el hilo de UI) o postInvalidate() (desde un hilo en segundo plano). El sistema decidirá cuándo llamar a onDraw y optimizará el dibujo combinando múltiples llamadas invalidate en un solo pase.

¿Por qué no se llama a onDraw después de invalidate?

Razones: la View está oculta (setVisibility(GONE) o INVISIBLE); la View no se ha agregado a la jerarquía de Window; el tamaño de la View es 0 (width o height = 0); se llama a setWillNotDraw(true) (true por defecto para ViewGroup). Verifique cada uno de estos puntos al depurar.

¿Con qué frecuencia se puede llamar a onDraw?

A 60 fps, onDraw se puede llamar hasta 60 veces por segundo. A 120 fps (en dispositivos con pantallas de 120 Hz) — hasta 120 veces. Cada llamada debe completarse en aproximadamente 8–16 milisegundos. Durante animaciones activas con ObjectAnimator o ValueAnimator, la frecuencia está determinada por el número de fotogramas de la animación.

¿Cuál es la diferencia entre onDraw y dispatchDraw?

onDraw es responsable de dibujar el contenido de la propia View. dispatchDraw es responsable de dibujar las View hijas (en ViewGroup). dispatchDraw se llama después de onDraw y recorre recursivamente todos los elementos hijos. Para una View normal (no ViewGroup), dispatchDraw está vacío.

¿Se puede usar Canvas fuera de onDraw?

, se puede crear un Canvas para un Bitmap mediante Canvas(bitmap) y dibujar en él en cualquier hilo. Esto se utiliza para prerrenderizar gráficos complejos en segundo plano. Sin embargo, el Canvas que se pasa a onDraw pertenece al sistema y solo se puede usar en el hilo de UI dentro del método onDraw.

Resumen

  • onDraw(Canvas) — un método clave de View para dibujo personalizado usando Canvas API en Android
  • Ciclo de dibujo consta de las fases measure, layout y draw — onDraw se llama en la fase draw
  • Canvas API incluye los métodos drawCircle, drawRect, drawPath, drawBitmap, drawText y otros
  • invalidate() solicita redibujo desde el hilo de UI, postInvalidate() desde un hilo en segundo plano
  • Redibujo parcial mediante invalidate(Rect) optimiza el renderizado actualizando solo la región sucia
  • Rendimiento de onDraw es crítico: crear objetos dentro del método provoca GC y caídas de fotogramas
  • Aceleración por hardware (desde API 11) acelera automáticamente las operaciones de Canvas a través de la GPU

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