Canvas dans Android — est une classe du package android.graphics fournissant une API pour le rendu 2D sur View. Canvas fonctionne comme une toile sur laquelle le développeur dessine des formes géométriques, du texte, des images et des chemins à l’aide de diverses méthodes draw*. Selon la Documentation des Développeurs Android (2025), Canvas est l’outil principal pour le rendu personnalisé dans Android et est utilisé avec la classe Paint pour définir les styles et les couleurs. Chaque appel onDraw() reçoit une instance Canvas prête, associée au buffer bitmap de la vue.
Points Clés
Canvas — est la classe centrale de graphiques 2D dans Android qui fournit des méthodes pour dessiner sur un plan. Chaque Canvas est lié à un Bitmap — une zone mémoire où les résultats des opérations de dessin sont enregistrés. Lorsqu’une View est redessinée, le système transmet à onDraw() un Canvas déjà associé au buffer de cette View.
Canvas fonctionne dans un système de coordonnées où (0,0) est le coin supérieur gauche, l’axe X va vers la droite, l’axe Y vers le bas. Toutes les opérations de dessin utilisent l’état actuel de Canvas, y compris la matrice de transformation, la zone de clipping et le style Paint. Selon la Documentation Android (2025), l’API Canvas comprend plus de 30 méthodes draw* pour différents types de primitives graphiques.
Depuis Android 3.0 (API 11), Canvas prend en charge l’accélération matérielle via DisplayList. En mode accéléré, les opérations Canvas sont enregistrées dans une DisplayList puis exécutées sur le GPU. Cela offre un gain de performance significatif mais impose des limitations : certaines opérations Canvas (clipPath avec clip non Path) ne sont pas prises en charge en mode accéléré.
Canvas fournit un ensemble de méthodes pour dessiner des primitives de base. Chaque méthode accepte des paramètres géométriques et un objet Paint qui définit le style visuel.
| Méthode | Objectif | Exemple |
|---|---|---|
| drawRect() | Dessine un rectangle | canvas.drawRect(10f, 10f, 100f, 100f, paint) |
| drawCircle() | Dessine un cercle | canvas.drawCircle(50f, 50f, 30f, paint) |
| drawLine() | Dessine une ligne | canvas.drawLine(0f, 0f, 100f, 100f, paint) |
| drawText() | Dessine du texte | canvas.drawText(“Hello”, 10f, 20f, paint) |
| drawBitmap() | Dessine une image | canvas.drawBitmap(bitmap, 0f, 0f, paint) |
| drawPath() | Dessine un chemin arbitraire | canvas.drawPath(path, paint) |
| drawOval() | Dessine un ovale | canvas.drawOval(rectF, paint) |
| drawArc() | Dessine un arc ou un secteur | canvas.drawArc(rectF, 0f, 90f, true, paint) |
Chaque méthode peut être appelée dans n’importe quel ordre — Canvas dessine dans l’ordre des appels, superposant les nouveaux éléments sur les précédents. Pour les compositions complexes, on utilise la technique de rendu multicouche avec sauvegarde et restauration de l’état de Canvas.
Paint — est la classe qui définit comment Canvas va dessiner. Sans Paint, les méthodes Canvas sont inutiles — elle définit la couleur, l’épaisseur du trait, le style de remplissage, la taille du texte, les effets (ombre, dégradé, flou) et le mode de mélange.
Paint a des propriétés clés : color (couleur), style (FILL, STROKE ou FILL_AND_STROKE), strokeWidth (épaisseur du trait), textSize (taille du texte), isAntiAlias (antialiasing), shader (dégradé ou image). Créer un objet Paint est une opération coûteuse, il doit donc être créé une fois et réutilisé.
private val fillPaint = Paint().apply {
color = Color.parseColor("#FF6200EE")
style = Paint.Style.FILL
isAntiAlias = true
}
private val strokePaint = Paint().apply {
color = Color.WHITE
style = Paint.Style.STROKE
strokeWidth = 4f
isAntiAlias = true
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.drawCircle(100f, 100f, 80f, fillPaint)
canvas.drawCircle(100f, 100f, 80f, strokePaint)
}
Dans cet exemple, deux objets Paint sont utilisés : un pour remplir le cercle en violet, le second pour le contour blanc. Séparer le remplissage et le contour est une pratique standard qui permet un contrôle flexible de l’apparence sans recréer les objets.
Pour les remplissages dégradés, Paint accepte un Shader — les sous-classes LinearGradient, RadialGradient, SweepGradient. Shader est lié à Paint via la méthode setShader() et définit comment la couleur change sur la zone de dessin.
Canvas prend en charge les transformations matricielles du système de coordonnées : translate() (translation), rotate() (rotation), scale() (échelle) et skew() (inclinaison). Les transformations s’accumulent : chaque appel suivant est appliqué par-dessus les précédents.
La gestion de la pile de transformations se fait via save() et restore(). save() sauvegarde l’état actuel de Canvas (matrice, clip) dans la pile, restore() restaure le dernier état sauvegardé. Entre save() et restore(), des transformations imbriquées peuvent être appliquées.
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.save() // Save initial state
canvas.translate(100f, 100f) // Translate origin
// First square
canvas.drawRect(0f, 0f, 50f, 50f, fillPaint)
canvas.save() // Save after translate
canvas.rotate(45f, 25f, 25f) // Rotate around square center
// Rotated square
canvas.drawRect(0f, 0f, 50f, 50f, strokePaint)
canvas.restore() // Restore after rotate
canvas.restore() // Restore initial state
}
Utilisez toujours save() avant de commencer les transformations et restore() après les avoir terminées. Les paires save/restore déséquilibrées entraînent une accumulation de la matrice et un rendu incorrect des éléments suivants.
Pour les graphiques complexes, Canvas est combiné avec Bitmap et Path. Bitmap est utilisé pour charger des images et mettre en cache les images rendues. Path est utilisé pour construire des courbes de Bézier arbitraires, des polygones et des contours complexes.
La technique de rendu hors écran : un Bitmap est créé, une scène complexe est dessinée dessus via un Canvas séparé, puis le Bitmap terminé est affiché dans onDraw() via drawBitmap(). Cela permet de mettre à jour des graphiques complexes sans redessiner complètement chaque image.
private var offscreenBitmap: Bitmap? = null
private var offscreenCanvas: Canvas? = null
fun buildOffscreenScene(width: Int, height: Int) {
offscreenBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
offscreenCanvas = Canvas(offscreenBitmap)
// Render scene once
offscreenCanvas!!.drawColor(Color.WHITE)
drawGrid(offscreenCanvas!!)
drawComplexShape(offscreenCanvas!!)
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
offscreenBitmap?.let { canvas.drawBitmap(it, 0f, 0f, null) }
}
Path est utilisé pour construire des contours complexes : courbes de Bézier (quadTo, cubicTo), arcs (arcTo), lignes (lineTo). Path peut être fermé (close()) pour les formes remplies ou ouvert pour les lignes. La méthode principale drawPath() combine Path et Paint pour le rendu final.
Les performances du rendu Canvas affectent directement les FPS de l’application. Les principaux problèmes : allocations excessives dans onDraw(), appels fréquents à invalidate() et utilisation sous-optimale de l’accélération matérielle.
Paint, Path, Rect, RectF et autres objets doivent être créés une fois dans l’initialiseur ou la méthode setup(). Chaque allocation dans onDraw() crée une pression sur le ramasse-miettes, entraînant des micro-ralentissements. L’exception concerne les objets static ou companion object pour les objets immuables.
Si la majeure partie de la View est cachée en dehors de la zone visible (par exemple, dans ScrollView), utilisez canvas.clipRect() pour limiter le rendu. L’accélération matérielle optimise alors supplémentairement le clipping des pixels invisibles.
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
// Clip to visible area
canvas.save()
canvas.clipRect(scrollX, scrollY, scrollX + width, scrollY + height)
// Render large scene
renderScene(canvas)
canvas.restore()
}
Utilisez View.setLayerType() avec LAYER_TYPE_HARDWARE pour les Views avec des graphiques Canvas complexes qui sont rarement mis à jour. Pour les animations fréquemment mises à jour, utilisez LAYER_TYPE_NONE pour éviter les mises à jour coûteuses de la couche matérielle. Profilez avec GPU Profile Rendering et Android Studio Profiler.
Foire Aux Questions
Oui, vous pouvez créer Canvas(Bitmap) — un constructeur qui accepte un Bitmap. Ceci est utilisé pour le rendu hors écran : vous dessinez sur un Bitmap, puis l’affichez via drawBitmap(). Canvas peut également être créé à partir de Surface (pour SurfaceView) ou via Picture.
Canvas — est une API pour dessiner sur View dans le thread UI. SurfaceView — est une Surface séparée qui peut être mise à jour depuis un thread d’arrière-plan via son propre Canvas (lockCanvas() / unlockCanvasAndPost()). SurfaceView est préférable pour les jeux et la vidéo.
La raison la plus fréquente est des paires save() et restore() déséquilibrées. Si après une transformation (translate, rotate) restore() n’est pas appelé, Canvas reste dans un système de coordonnées modifié et les éléments suivants sont dessinés avec un décalage. Vérifiez vos paires save()/restore().
Utilisez Paint.setTextAlign(Paint.Align.CENTER) pour le centrage horizontal et Paint.getTextBounds() ou Paint.descent() / ascent() pour le vertical. Centrage : canvas.drawText(texte, centerX, centerY - (ascent + descent) / 2, paint).
Canvas lui-même n’anime pas — il dessine seulement l’état actuel. L’animation est obtenue par des appels séquentiels à invalidate() (ou postInvalidateOnAnimation()), dessinant chaque fois une nouvelle image dans onDraw(). Pour des animations fluides, utilisez ValueAnimator ou Choreographer pour vous synchroniser avec Vsync.
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