dispatchDraw() est une méthode de la classe ViewGroup responsable du dessin récursif de toutes les vues filles dans la hiérarchie Android. Selon la documentation développeurs Android (2026), dispatchDraw est appelé automatiquement après onDraw dans la méthode draw() de la vue principale et parcourt tous les éléments enfants, appelant leur propre draw(). Les développeurs redéfinissent dispatchDraw dans des ViewGroups personnalisés pour ajouter des effets, superposer des graphiques sur les enfants ou modifier l'ordre de dessin.
Points clés
dispatchDraw(Canvas canvas) est une méthode protected de la classe ViewGroup que le système Android appelle pour dessiner toutes les vues filles du conteneur actuel. dispatchDraw fait partie du pipeline de dessin standard : d'abord onDraw s'exécute (dessin du contenu de la vue elle-même), puis dispatchDraw (dessin récursif des enfants), puis onDrawForeground (dessin du premier plan et des barres de défilement). La méthode n'est pas destinée à être appelée directement depuis le code de l'application.
L'implémentation standard de dispatchDraw dans ViewGroup parcourt toutes les vues filles, vérifie leur visibilité et appelle draw(Canvas) pour chacune. L'ordre de parcours correspond aux indices des enfants (de 0 à childCount - 1). Si les vues filles se chevauchent, les dernières de la liste sont dessinées par-dessus les premières. Modifier l'ordre dans dispatchDraw permet de changer le Z-order du dessin.
Pour une View (pas ViewGroup), la méthode dispatchDraw est vide — une vue normale n'a pas d'éléments enfants, donc rien à dessiner. La méthode existe dans la classe de base View mais n'est appelée que sur les instances de ViewGroup. Un développeur peut vérifier si une vue a des éléments enfants via le comportement par défaut de dispatchDraw, mais il est souvent plus simple de vérifier instanceof ViewGroup.
La méthode draw(Canvas) de la classe View organise un pipeline de dessin en trois étapes. La première étape est l'appel de onDraw(canvas), où la vue dessine son propre contenu. La deuxième étape est l'appel de dispatchDraw(canvas), qui déclenche le dessin des vues filles. La troisième étape est onDrawForeground(canvas), responsable de la couche de premier plan, des barres de défilement et des effets d'ondulation. Cette séquence garantit l'ordre correct de superposition des couches graphiques.
onDraw est toujours exécuté avant dispatchDraw. Cela signifie que le contenu de la vue parent est affiché sous les vues filles. Si vous devez dessiner quelque chose par-dessus les enfants, cela se fait dans onDrawForeground ou dans un dispatchDraw redéfini en appelant super.dispatchDraw(canvas) puis en dessinant par-dessus. L'arrière-plan est dessiné avant même onDraw — dans drawBackground(canvas) à l'intérieur de la méthode draw().
Avec l'accélération matérielle, le pipeline de dessin fonctionne via le GPU. La vue est dessinée dans un DisplayList — une liste de commandes de dessin qui est mise en cache et réutilisée. dispatchDraw en mode accéléré matériel ajoute les DisplayLists des vues filles au DisplayList global de la scène. La modification de dispatchDraw peut invalider le DisplayList et provoquer une reconstruction du cache, ce qui impacte les performances.
| Méthode | Ordre d'appel | Objectif |
|---|---|---|
| drawBackground | 1 | Dessin de l'arrière-plan (background drawable) |
| onDraw | 2 | Dessin du contenu de la vue elle-même |
| dispatchDraw | 3 | Dessin de toutes les vues filles (ViewGroup uniquement) |
| onDrawForeground | 4 | Dessin du premier plan, barre de défilement et effet d'ondulation |
onDraw est responsable du dessin du contenu de la vue elle-même — formes, texte, images qui appartiennent à cet élément spécifique. dispatchDraw est responsable du dessin des vues filles — tous les éléments contenus dans le ViewGroup. Si un ViewGroup ne redéfinit pas dispatchDraw, l'implémentation de ViewGroup est utilisée, qui parcourt simplement récursivement childCount et appelle draw pour chaque enfant.
Pour un ViewGroup qui ne dessine pas son propre contenu (par exemple, FrameLayout, LinearLayout), onDraw peut être optimisé en définissant setWillNotDraw(true). Dans ce cas, onDraw n'est jamais appelé, ce qui économise des ressources. dispatchDraw continue de fonctionner et appelle draw pour les éléments enfants. Tous les ViewGroups standard (LinearLayout, RelativeLayout, ConstraintLayout) utilisent setWillNotDraw(true).
Si vous redéfinissez dispatchDraw sans appeler super.dispatchDraw(canvas), les vues filles ne seront pas dessinées. Cela peut être utile pour masquer temporairement tous les enfants, mais dans la plupart des cas, cela provoque des erreurs. Pratique recommandée : appelez super.dispatchDraw(canvas) au début de la méthode redéfinie, puis ajoutez vos propres graphiques (par exemple, une superposition avec canal alpha) par-dessus les enfants.
Créons un OverlayViewGroup — un ViewGroup personnalisé qui ajoute une couche semi-transparente avec un libellé de texte par-dessus toutes les vues filles. dispatchDraw appelle d'abord super (dessin de tous les enfants), puis dessine le rectangle de superposition et le texte. Paint est créé dans le constructeur pour éviter les allocations dans la boucle de dessin.
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("MODE AVANT-PREMIÈRE",
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))
}
}
L'exemple suivant démontre le changement d'ordre du dessin des éléments enfants. CircularRevealLayout redéfinit dispatchDraw et dessine les enfants dans l'ordre inverse, créant un effet de Z-order inversé. Pour l'animation, un décalage cyclique de l'index de dessin est ajouté basé sur la valeur animée 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 est utilisé dans les scénarios où vous devez influencer le processus de dessin des vues filles sans affecter le contenu propre du ViewGroup. Principaux scénarios : appliquer un effet commun (superposition, ombre, dégradé) par-dessus tous les enfants ; modifier le Z-order pour créer des effets de profondeur ; animer l'apparition ou la disparition d'éléments enfants via des transformations Canvas.
Si vous devez appliquer des graphiques en dessous des éléments enfants (effet d'arrière-plan), utilisez onDraw — dispatchDraw est appelé après. Si les graphiques doivent être au-dessus des enfants — utilisez dispatchDraw en appelant super d'abord, puis Canvas.draw. Si vous avez besoin d'un changement global de couleur ou de transparence, il est pratique d'utiliser canvas.saveLayerAlpha() dans dispatchDraw, en encapsulant le dessin des enfants.
Non recommandé d'utiliser dispatchDraw pour : 1) dessiner des animations complexes en temps réel (utilisez invalidate et onDraw) ; 2) créer des captures d'écran d'un ViewGroup (utilisez buildDrawingCache() ou View.draw(Canvas)) ; 3) modifier les vues filles (coordonnées, tailles — ce sont des tâches de onLayout, pas de dispatchDraw). dispatchDraw est uniquement pour appliquer des effets visuels.
Questions fréquentes
Non, dispatchDraw est une méthode protected appelée par le système Android à l'intérieur de la méthode publique draw(). Un appel direct à dispatchDraw n'a pas de sens car il n'effectue pas d'actions préparatoires (sauvegarde du Canvas, dessin de l'arrière-plan et du premier plan). Utilisez plutôt View.draw(Canvas) pour un dessin programmatique sur un Canvas arbitraire.
Si vous appelez super.dispatchDraw(canvas) après vos propres opérations de dessin, les vues filles seront dessinées par-dessus les graphiques personnalisés. Cela change l'ordre des couches : d'abord la couche personnalisée est dessinée, puis les enfants. Si vous avez besoin de la situation inverse (graphiques par-dessus les enfants), appelez d'abord super.dispatchDraw, puis vos propres opérations.
dispatchDraw appelle draw() pour chaque vue fille, et le temps total est proportionnel au nombre d'enfants. Avec l'accélération matérielle, ajouter des opérations Canvas dans dispatchDraw peut invalider le DisplayList et provoquer une reconstruction du cache. Pour 5 à 10 enfants, l'impact est minime, pour 50+ enfants, il est recommandé de mettre en cache les superpositions complexes.
Une View (pas ViewGroup) n'a pas d'éléments enfants, donc dispatchDraw n'effectue aucun travail utile. Cependant, la méthode existe dans la classe de base View pour le polymorphisme : le code dans draw() appelle dispatchDraw pour toute View, mais l'implémentation de View ne contient pas de logique de dessin d'enfants. Seul ViewGroup redéfinit dispatchDraw.
Oui, dispatchDraw accepte un Canvas et il peut être transformé avant d'appeler super.dispatchDraw (translate, rotate, scale). Cela est utilisé pour animer l'ensemble des enfants comme une seule unité. Après la fin de super.dispatchDraw, il est recommandé de restaurer le Canvas à son état d'origine.
Résumé
Nous développerons une application mobile clé en main
IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.
Lisez aussi