dispatchDraw(): qué es, mecanismo de llamada y diferencia con onDraw

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

dispatchDraw() es un método de la clase ViewGroup responsable del dibujo recursivo de todas las vistas hijas en la jerarquía de Android. Según la documentación para desarrolladores de Android (2026), dispatchDraw se llama automáticamente después de onDraw en el método draw() de la vista principal y recorre todos los elementos hijos, llamando a su propio draw(). Los desarrolladores sobrescriben dispatchDraw en ViewGroups personalizados para añadir efectos, superponer gráficos sobre los hijos o cambiar el orden de dibujo.

Puntos clave

  • dispatchDraw(Canvas canvas) es un método protected de ViewGroup llamado después de onDraw para dibujar las vistas hijas
  • draw(Canvas canvas) es un método público final de View que llama secuencialmente a onDraw, dispatchDraw y onDrawForeground
  • onDraw dibuja el contenido de la propia vista, mientras que dispatchDraw dibuja todos los elementos hijos
  • setWillNotDraw(true) es una optimización para ViewGroup que no dibuja su propio contenido, solo los elementos hijos
  • Sobrescribir dispatchDraw se usa para aplicar efectos (blur, overlay) o animar el dibujo de elementos hijos

¿Qué es dispatchDraw()?

dispatchDraw(Canvas canvas) es un método protected de la clase ViewGroup que el sistema Android llama para dibujar todas las vistas hijas del contenedor actual. dispatchDraw forma parte del pipeline estándar de draw: primero se ejecuta onDraw (dibujo del contenido de la propia vista), luego dispatchDraw (dibujo recursivo de los hijos), luego onDrawForeground (dibujo del foreground y scrollbars). El método no está diseñado para ser llamado directamente desde el código de la aplicación.

La implementación estándar de dispatchDraw en ViewGroup recorre todas las vistas hijas, verifica su visibilidad y llama a draw(Canvas) para cada una. El orden de recorrido corresponde a los índices de los hijos (de 0 a childCount - 1). Si las vistas hijas se superponen, las últimas en la lista se dibujan encima de las primeras. Cambiar el orden en dispatchDraw permite modificar el Z-order del dibujo.

Para una View (no ViewGroup), el método dispatchDraw está vacío — una vista normal no tiene elementos hijos, por lo que no hay nada que dibujar. El método existe en la clase base View pero solo se llama en instancias de ViewGroup. Un desarrollador puede verificar si una vista tiene elementos hijos a través del comportamiento predeterminado de dispatchDraw, pero a menudo es más simple comprobar instanceof ViewGroup.

Pipeline de dibujo: onDraw, dispatchDraw, onDrawForeground

El método draw(Canvas) de la clase View organiza un pipeline de dibujo de tres etapas. La primera etapa es llamar a onDraw(canvas), donde la vista dibuja su propio contenido. La segunda etapa es llamar a dispatchDraw(canvas), que desencadena el dibujo de las vistas hijas. La tercera etapa es onDrawForeground(canvas), responsable de la capa de foreground, scrollbars y efectos ripple. Esta secuencia garantiza el orden correcto de superposición de las capas gráficas.

onDraw siempre se ejecuta antes que dispatchDraw. Esto significa que el contenido de la vista padre se muestra debajo de las vistas hijas. Si necesita dibujar algo encima de los hijos, esto se hace en onDrawForeground o en un dispatchDraw sobrescrito llamando a super.dispatchDraw(canvas) y luego dibujando encima. El fondo se dibuja incluso antes de onDraw — en drawBackground(canvas) dentro del método draw().

Con la aceleración por hardware, el pipeline de draw funciona a través de la GPU. La vista se dibuja en un DisplayList — una lista de comandos de dibujo que se almacena en caché y se reutiliza. dispatchDraw en modo acelerado por hardware añade los DisplayLists de las vistas hijas al DisplayList general de la escena. Modificar dispatchDraw puede invalidar el DisplayList y provocar la reconstrucción de la caché, lo que afecta al rendimiento.

MétodoOrden de llamadaPropósito
drawBackground1Dibujo del fondo (background drawable)
onDraw2Dibujo del contenido de la propia vista
dispatchDraw3Dibujo de todas las vistas hijas (solo ViewGroup)
onDrawForeground4Dibujo de foreground, scrollbar y efecto ripple

Diferencia entre dispatchDraw y onDraw

onDraw es responsable de dibujar el contenido de la propia vista — formas, texto, imágenes que pertenecen a este elemento específico. dispatchDraw es responsable de dibujar las vistas hijas — todos los elementos contenidos dentro del ViewGroup. Si un ViewGroup no sobrescribe dispatchDraw, se usa la implementación de ViewGroup, que simplemente recorre recursivamente childCount y llama a draw para cada hijo.

Para un ViewGroup que no dibuja su propio contenido (por ejemplo, FrameLayout, LinearLayout), onDraw se puede optimizar estableciendo setWillNotDraw(true). En este caso, onDraw nunca se llama, lo que ahorra recursos. dispatchDraw continúa funcionando y llama a draw para los elementos hijos. Todos los ViewGroups estándar (LinearLayout, RelativeLayout, ConstraintLayout) usan setWillNotDraw(true).

Si sobrescribe dispatchDraw sin llamar a super.dispatchDraw(canvas), las vistas hijas no se dibujarán. Esto puede ser útil para ocultar temporalmente todos los hijos, pero en la mayoría de los casos causa errores. Práctica recomendada: llamar a super.dispatchDraw(canvas) al principio del método sobrescrito, y luego agregar sus propios gráficos (por ejemplo, una superposición con canal alfa) encima de los hijos.

Ejemplo de sobrescritura de dispatchDraw en Kotlin

Creemos un OverlayViewGroup — un ViewGroup personalizado que agrega una capa semitransparente con una etiqueta de texto encima de todas las vistas hijas. dispatchDraw primero llama a super (dibujando todos los hijos), luego dibuja el rectángulo de superposición y el texto. Paint se crea en el constructor para evitar asignaciones en el bucle de draw.

kotlin
class OverlayViewGroup(context: Context)
    : ViewGroup(context) {

    private val overlayPaint = Paint().apply {
        color = Color.parseColor("#66000000")
        style = Paint.Style.FILL
    }

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

    init {
        setWillNotDraw(false)
    }

    override fun dispatchDraw(canvas: Canvas) {
        super.dispatchDraw(canvas)
        canvas.drawRect(0f, 0f,
                        width.toFloat(),
                        height.toFloat(), overlayPaint)
        canvas.drawText("MODO VISTA PREVIA",
            width / 2f,
            height / 2f, textPaint)
    }

    override fun onLayout(changed: Boolean,
                       l: Int, t: Int,
                       r: Int, b: Int) {
        var top = t
        for (i in 0 until childCount) {
            val child = getChildAt(i)
            val cw = child.measuredWidth
            val ch = child.measuredHeight
            child.layout(l, top, l + cw, top + ch)
            top += ch
        }
    }

    override fun onMeasure(widthMeasureSpec: Int,
                       heightMeasureSpec: Int) {
        measureChildren(widthMeasureSpec, heightMeasureSpec)
        val maxWidth = resolveSize(
            getChildAt(0).measuredWidth,
            widthMeasureSpec)
        var totalHeight = 0
        for (i in 0 until childCount) {
            totalHeight += getChildAt(i).measuredHeight
        }
        setMeasuredDimension(maxWidth,
            resolveSize(totalHeight, heightMeasureSpec))
    }
}

Z-order animado mediante dispatchDraw

El siguiente ejemplo demuestra cambiar el orden de dibujo de los elementos hijos. CircularRevealLayout sobrescribe dispatchDraw y dibuja los hijos en orden inverso, creando un efecto de Z-order invertido. Para la animación, se agrega un desplazamiento cíclico del índice de dibujo basado en el valor animado offset.

kotlin
class ReverseOrderLayout(context: Context)
    : ViewGroup(context) {

    private var reverse = false

    override fun dispatchDraw(canvas: Canvas) {
        if (!reverse) {
            super.dispatchDraw(canvas)
            return
        }
        for (i in childCount - 1 downTo 0) {
            val child = getChildAt(i)
            if (child.visibility == GONE) continue
            drawChild(canvas, child, getDrawingTime())
        }
    }

    fun toggleReverse() {
        reverse = !reverse
        invalidate()
    }

    override fun onLayout(changed: Boolean,
                       l: Int, t: Int,
                       r: Int, b: Int) {
        var left = l
        for (i in 0 until childCount) {
            val child = getChildAt(i)
            val cw = child.measuredWidth
            child.layout(left, t,
                left + cw, t + child.measuredHeight)
            left += cw
        }
    }

    override fun onMeasure(widthMeasureSpec: Int,
                       heightMeasureSpec: Int) {
        measureChildren(widthMeasureSpec, heightMeasureSpec)
        var totalWidth = 0
        for (i in 0 until childCount) {
            totalWidth += getChildAt(i).measuredWidth
        }
        setMeasuredDimension(
            resolveSize(totalWidth, widthMeasureSpec),
            resolveSize(getChildAt(0).measuredHeight,
                heightMeasureSpec))
    }
}

Cuándo usar dispatchDraw en Android

dispatchDraw se usa en escenarios donde se necesita influir en el proceso de dibujo de las vistas hijas sin afectar el contenido propio del ViewGroup. Escenarios principales: aplicar un efecto común (overlay, shadow, gradient) encima de todos los hijos; cambiar el Z-order para crear efectos de profundidad; animar la aparición o desaparición de elementos hijos mediante transformaciones de Canvas.

Si se necesita aplicar gráficos debajo de los elementos hijos (efecto de fondo), use onDraw — dispatchDraw se llama después. Si los gráficos deben estar encima de los hijos — use dispatchDraw llamando primero a super, luego a Canvas.draw. Si se necesita un cambio global de color o transparencia, es conveniente usar canvas.saveLayerAlpha() en dispatchDraw, envolviendo el dibujo de los hijos.

No se recomienda usar dispatchDraw para: 1) dibujar animaciones complejas en tiempo real (use invalidate y onDraw); 2) crear capturas de pantalla de un ViewGroup (use buildDrawingCache() o View.draw(Canvas)); 3) modificar vistas hijas (coordenadas, tamaños — son tareas de onLayout, no de dispatchDraw). dispatchDraw es solo para aplicar efectos visuales.

Preguntas frecuentes

¿Se puede llamar a dispatchDraw directamente sin el sistema?

No, dispatchDraw es un método protected llamado por el sistema Android dentro del método público draw(). Una llamada directa a dispatchDraw no tiene sentido porque no realiza acciones preparatorias (guardar Canvas, dibujar fondo y foreground). En su lugar, use View.draw(Canvas) para dibujar programáticamente en un Canvas arbitrario.

¿Qué sucede cuando se llama a super.dispatchDraw después del dibujo personalizado?

Si llama a super.dispatchDraw(canvas) después de sus propias operaciones de dibujo, las vistas hijas se dibujarán encima de los gráficos personalizados. Esto cambia el orden de las capas: primero se dibuja la capa personalizada, luego los hijos. Si necesita la situación opuesta (gráficos encima de los hijos), primero llame a super.dispatchDraw, luego a sus propias operaciones.

¿Cómo afecta dispatchDraw al rendimiento?

dispatchDraw llama a draw() para cada vista hija, y el tiempo total es proporcional al número de hijos. Con aceleración por hardware, agregar operaciones de Canvas en dispatchDraw puede invalidar el DisplayList y provocar la reconstrucción de la caché. Para 5–10 hijos el impacto es mínimo, para más de 50 hijos se recomienda almacenar en caché las superposiciones complejas.

¿Por qué no se llama a dispatchDraw para una View?

Una View (no ViewGroup) no tiene elementos hijos, por lo que dispatchDraw no realiza ningún trabajo útil. Sin embargo, el método existe en la clase base View para polimorfismo: el código en draw() llama a dispatchDraw para cualquier View, pero la implementación de View no contiene lógica de dibujo de hijos. Solo ViewGroup sobrescribe dispatchDraw.

¿Se puede modificar el Canvas en dispatchDraw?

, dispatchDraw acepta un Canvas y se puede transformar antes de llamar a super.dispatchDraw (translate, rotate, scale). Esto se usa para animar todo el conjunto de hijos como una sola unidad. Después de que super.dispatchDraw se complete, se recomienda restaurar el Canvas a su estado original.

Resumen

  • dispatchDraw(Canvas) es un método de ViewGroup para el dibujo recursivo de todas las vistas hijas después de onDraw
  • Pipeline de draw: drawBackground → onDraw → dispatchDraw → onDrawForeground
  • onDraw dibuja el contenido de la propia vista, dispatchDraw dibuja el contenido de los elementos hijos
  • setWillNotDraw(true) optimiza ViewGroup omitiendo onDraw, pero dispatchDraw continúa funcionando
  • Sobrescribir dispatchDraw se usa para aplicar efectos, cambiar Z-order y transformaciones de Canvas en los hijos
  • El orden de super.dispatchDraw y el dibujo personalizado determina qué aparece encima de los hijos
  • Para una View normal (no ViewGroup) dispatchDraw está vacío — no tiene elementos hijos

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