onDraw() est une méthode clé de la classe View dans le SDK Android responsable du dessin du contenu d’une vue. Le système appelle onDraw à chaque mise à jour de l’apparence de la View — lors du rendu initial, après invalidate() ou lors d’un changement de dimensions. Selon la documentation Android Developers (2026), la redéfinition d’onDraw avec l’API Canvas est le moyen principal de créer des graphiques personnalisés dans les applications Android.
Points essentiels
onDraw(Canvas canvas) est une méthode protected de la classe android.view.View, que le système Android appelle lorsqu’une View doit être dessinée. Le développeur redéfinit cette méthode pour implémenter un dessin personnalisé : formes, texte, images, dégradés, animations. Le paramètre Canvas est une surface graphique sur laquelle toutes les opérations de dessin sont exécutées.
La signature de la méthode est simple : override fun onDraw(canvas: Canvas). À l’intérieur de cette méthode, il ne faut pas créer de nouveaux objets (pour éviter la charge du GC), effectuer des opérations longues ou travailler avec le réseau. View inclut également la méthode onDraw dans son cycle de vie entre onLayout (calcul des positions) et dispatchDraw (dessin des éléments enfants).
En héritant de View, la redéfinition d’onDraw est obligatoire pour afficher un contenu — sinon la View sera vide. En héritant de sous-classes existantes (TextView, ImageView, Button), la redéfinition d’onDraw est optionnelle et sert à ajouter des graphiques supplémentaires par-dessus le contenu standard.
Android effectue le dessin de la View dans une séquence strictement définie appelée depuis le thread principal (UI thread). Le cycle complet se compose de trois phases : measure (mesure des dimensions), layout (calcul des positions) et draw (rendu). onDraw fait partie de la phase draw et est appelé après que les dimensions et la position de la View ont été déterminées.
draw(Canvas canvas) est une méthode publique de View qui appelle onDraw et dispatchDraw. La méthode draw n’est pas conçue pour être redéfinie — c’est onDraw qui est redéfini. Après l’exécution d’onDraw pour la View actuelle, dispatchDraw est appelé, ce qui invoque récursivement draw pour toutes les Views enfants (si la View actuelle est un conteneur ViewGroup).
onDraw est appelé dans les conditions suivantes : 1) la View devient visible pour la première fois (après setVisibility(VISIBLE)) ; 2) l’application appelle invalidate() sur cette View ou son parent ; 3) la taille de la View change (nouveau layout) ; 4) une animation associée à la View change (via ValueAnimator ou ObjectAnimator) ; 5) l’état de la View change (pressed, focused, enabled).
Depuis Android 3.0 (API 11), la plupart des opérations Canvas sont accélérées par le matériel via le GPU. Le développeur peut vérifier si l’accélération est activée via canvas.isHardwareAccelerated(). Avec l’accélération matérielle, certaines opérations Canvas ne sont pas prises en charge (par exemple, clipPath avec une région non rectangulaire) et peuvent être ignorées ou provoquer des erreurs.
Canvas fournit un riche ensemble de méthodes de dessin. Toutes les méthodes fonctionnent dans le système de coordonnées actuel, qui peut être modifié par des transformations (translate, rotate, scale, skew). Chaque méthode accepte un objet Paint qui définit la couleur, le style, l’épaisseur de trait, les effets et l’anticrénelage.
| Méthode Canvas | Objectif | Paramètres clés de Paint |
|---|---|---|
| drawCircle | Dessiner un cercle | centerX, centerY, radius, paint |
| drawRect | Dessiner un rectangle | left, top, right, bottom, paint |
| drawLine | Dessiner une ligne | startX, startY, endX, endY, paint |
| drawPath | Dessiner un chemin arbitraire | path, paint |
| drawBitmap | Dessiner une image bitmap | bitmap, srcRect, dstRect, paint |
| drawText | Dessiner du texte | text, x, y, paint |
| drawArc | Dessiner un arc ou un secteur | ovalRect, startAngle, sweepAngle, useCenter, paint |
| drawOval | Dessiner un ovale | ovalRect, paint |
Paint définit le style de dessin : couleur (setColor), épaisseur de trait (setStrokeWidth), style de remplissage (setStyle — FILL, STROKE, FILL_AND_STROKE), anticrénelage (setAntiAlias), effets (setShadowLayer, setMaskFilter) et dégradés (setShader). Il est recommandé de créer et configurer Paint une fois dans le constructeur de la View, pas dans onDraw, pour éviter la création d’objets dans la boucle de dessin.
Canvas utilise un système de coordonnées où l’axe X pointe vers la droite et l’axe Y vers le bas. L’origine (0, 0) est le coin supérieur gauche de la View. Avant d’appeler les méthodes de dessin, le développeur peut modifier le système de coordonnées via canvas.translate(dx, dy), canvas.rotate(degrees, px, py), canvas.scale(sx, sy, px, py). Toutes les opérations de dessin ultérieures seront effectuées dans le système transformé.
onDraw s’exécute sur le thread UI principal, et sa durée affecte directement le taux d’images (fps). Pour maintenir 60 fps, la méthode onDraw doit se terminer en moins de 16 millisecondes. Tout ralentissement entraîne des chutes d’images (jank), qui se manifestent visuellement par un bégaiement de l’animation ou un retard de défilement.
Règles principales pour l’optimisation d’onDraw : 1) ne pas créer d’objets dans onDraw — tous les objets Paint, Path, Rect doivent être des champs de classe ; 2) ne pas allouer de mémoire (new) — cela déclenche le ramasse-miettes (GC) et des chutes d’images ; 3) mettre en cache les opérations complexes dans un Bitmap via Bitmap.createBitmap et dessiner le Bitmap préparé ; 4) ne pas effectuer d’opérations d’E/S ou de chargement de ressources.
clipRect limite la zone de dessin à un rectangle spécifié. Il empêche Canvas de perdre du temps à dessiner des pixels en dehors de la zone visible. Particulièrement efficace lors du défilement de grandes listes ou du zoom : définissez clipRect sur la zone visible (dirty rect), et Canvas ne dessinera que ce qui est visible.
canvas.save() sauvegarde l’état actuel du Canvas (transformation et clip) sur une pile. canvas.restore() restaure le dernier état sauvegardé. Nécessaire pour les transformations temporaires : sauvegardez l’état, appliquez translate/rotate, dessinez, puis restaurez. save et restore sont légers et n’affectent pas les performances s’ils sont utilisés raisonnablement (moins de 10–15 niveaux d’imbrication).
invalidate() est une méthode de View qui informe le système que le contenu de la View est obsolète et doit être redessiné. L’appel à invalidate initie un nouveau cycle de draw dans lequel onDraw sera appelé. invalidate ne doit être appelé que depuis le thread UI (thread principal). Pour l’appel depuis un thread d’arrière-plan, utilisez postInvalidate.
postInvalidate() est une version thread-safe d’invalidate qui envoie une demande de redessin à la file d’attente du thread principal via Handler. Utilisée dans les tâches d’arrière-plan lorsque la View doit être mise à jour après le chargement de données ou des calculs. Peut être combinée avec runOnUiThread pour la synchronisation avec l’UI.
Pour un redessin partiel, utilisez invalidate(Rect dirty) ou invalidate(int l, int t, int r, int b). Cela indique au système que seule la zone spécifiée a changé, pas la View entière. Android optimise le dessin en ne redessinant que le dirty rect, ce qui améliore considérablement les performances des animations avec des mises à jour partielles.
Voyons des exemples pratiques de redéfinition d’onDraw pour créer des Views personnalisées dans Android avec Kotlin.
Cet exemple crée un indicateur de progression circulaire avec remplissage animé. onDraw dessine deux cercles : un cercle de fond gris et un cercle de remplissage coloré dont l’arc est déterminé par la progression actuelle. L’appel à invalidate dans setProgress garantit le redessin lorsque la valeur change.
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)
}
}
Cet exemple dessine un rectangle avec un dégradé et du texte par-dessus. Le dégradé est créé via LinearGradient (Shader), et le texte via drawText. Les deux objets Paint sont créés une fois dans le constructeur, tandis qu’onDraw ne fait que mettre à jour la taille du rectangle en fonction de la largeur actuelle de la View.
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)
}
}
Cet exemple crée une horloge animée qui met à jour le dessin toutes les 1000 millisecondes. postDelayed dans onDraw appelle invalidate après une seconde, créant une boucle de redessin infinie. Les aiguilles de l’horloge sont dessinées via drawLine avec différentes longueurs et épaisseurs.
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)
}
}
Questions fréquentes
Non, il ne faut pas appeler onDraw directement. Pour demander un redessin, utilisez invalidate() (depuis le thread UI) ou postInvalidate() (depuis un thread d’arrière-plan). Le système décidera quand appeler onDraw et optimisera le dessin en fusionnant plusieurs appels invalidate en un seul passage.
Raisons : la View est cachée (setVisibility(GONE) ou INVISIBLE) ; la View n’a pas été ajoutée à la hiérarchie de la Window ; la taille de la View est 0 (largeur ou hauteur = 0) ; setWillNotDraw(true) est appelé (true par défaut pour ViewGroup). Vérifiez chacun de ces points lors du débogage.
À 60 fps, onDraw peut être appelé jusqu’à 60 fois par seconde. À 120 fps (sur les appareils avec écrans 120 Hz) — jusqu’à 120 fois. Chaque appel doit se terminer en environ 8–16 millisecondes. Pendant les animations actives via ObjectAnimator ou ValueAnimator, la fréquence est déterminée par le nombre d’images de l’animation.
onDraw est responsable du dessin du contenu de la View elle-même. dispatchDraw est responsable du dessin des Views enfants (dans ViewGroup). dispatchDraw est appelé après onDraw et parcourt récursivement tous les éléments enfants. Pour une View ordinaire (pas ViewGroup), dispatchDraw est vide.
Oui, on peut créer un Canvas pour un Bitmap via Canvas(bitmap) et dessiner dessus dans n’importe quel thread. Cela permet de pré-rendre des graphiques complexes en arrière-plan. Cependant, le Canvas passé à onDraw appartient au système et ne peut être utilisé que sur le thread UI dans la méthode onDraw.
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