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 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.
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étodo | Orden de llamada | Propósito |
|---|---|---|
| drawBackground | 1 | Dibujo del fondo (background drawable) |
| onDraw | 2 | Dibujo del contenido de la propia vista |
| dispatchDraw | 3 | Dibujo de todas las vistas hijas (solo ViewGroup) |
| onDrawForeground | 4 | Dibujo de foreground, scrollbar y efecto ripple |
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.
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.
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))
}
}
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.
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))
}
}
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
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.
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.
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.
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.
Sí, 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
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.
Lea también