invalidate() est une méthode de la classe View dans Android qui marque une vue comme nécessitant un redessin. L'appel à invalidate() déclenche un redessin de la vue dans le prochain cycle de rafraîchissement de l'écran, ce qui en fait le mécanisme principal de mise à jour de l'état visuel des composants personnalisés. Selon la documentation Android Developers (2025), invalidate() est utilisé dans 90 % des vues personnalisées pour synchroniser les changements de données avec l'affichage à l'écran. La méthode fonctionne de manière asynchrone — elle définit simplement le drapeau dirty et retourne immédiatement le contrôle.
Points clés
invalidate() est une méthode de la classe android.view.View qui informe le système Android que la représentation visuelle d'une vue est obsolète. Après avoir appelé la méthode, le système marque la vue comme dirty et planifie son redessin dans le prochain cycle de rafraîchissement de l'écran (généralement 16 ms pour 60 FPS).
La méthode invalidate() se présente sous différentes formes : sans paramètre (redessin complet), avec un paramètre Rect (partiel) et avec des paramètres ltrb (left, top, right, bottom). Toutes les versions fonctionnent de manière asynchrone et doivent être appelées depuis le thread UI. Pour les appels depuis des threads d'arrière-plan, il existe postInvalidate().
Le mécanisme de redessin dans Android est basé sur ViewRootImpl — un composant interne qui connecte la hiérarchie des vues à la surface de dessin. Lorsqu'invalidate() est appelé, ViewRootImpl marque la zone de la vue comme dirty et envoie une demande de redessin via Choreographer — un service système qui synchronise le dessin avec la fréquence de rafraîchissement de l'écran.
Choreographer reçoit un signal de Vsync et lance un triple passage : measure, layout, draw. Cependant, invalidate() n'affecte que la phase draw — les phases measure et layout ne sont pas exécutées à moins que requestLayout() n'ait été appelé. C'est une différence clé : invalidate() est plus léger que requestLayout() car il ne recalcule pas la géométrie.
class CustomChartView(context: Context, attrs: AttributeSet?)
: View(context, attrs) {
private var dataPoints: List<Float> = emptyList()
private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
fun updateData(newPoints: List<Float>) {
dataPoints = newPoints
invalidate() // Demande de redessin
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
paint.color = Color.BLUE
paint.strokeWidth = 4f
paint.style = Paint.Style.STROKE
// Dessin de la ligne du graphique
val path = Path()
dataPoints.forEachIndexed { index, value ->
val x = index * width / max(dataPoints.size - 1, 1)
val y = height - value * height
if (index == 0) path.moveTo(x, y)
else path.lineTo(x, y)
}
canvas.drawPath(path, paint)
}
}
Dans cet exemple, une vue personnalisée pour dessiner un graphique appelle invalidate() lorsque les données sont mises à jour. Le système ne redessine que cette vue sans affecter les autres éléments de la hiérarchie. onDraw() reçoit un Canvas pour dessiner des lignes via Path.
La principale différence entre invalidate() et postInvalidate() réside dans la sécurité des threads. invalidate() ne doit être appelé que depuis le thread UI (thread principal). postInvalidate() peut être appelé depuis n'importe quel thread — il envoie une demande de redessin au thread UI via Handler.
| Caractéristique | invalidate() | postInvalidate() |
|---|---|---|
| Thread d'appel | Thread UI (thread principal) | N'importe quel thread |
| Mécanisme | Mise à jour directe du drapeau dirty | Via Handler.post() vers le thread UI |
| Latence | Minimale, dans le cycle actuel | Jusqu'au prochain cycle du thread UI |
| Performance | Élevée | Léger overhead de Handler |
| Recommandation | Toujours invalidate() pour le thread UI | Uniquement pour les threads d'arrière-plan |
En pratique, postInvalidate() est utilisé dans des scénarios de chargement de données réseau, de traitement de résultats de capteurs ou de calculs en arrière-plan. Si vous êtes dans le thread UI — utilisez toujours invalidate() pour une latence minimale.
// Appelé depuis le thread UI
view.invalidate()
// Appelé depuis le thread d'arrière-plan
Thread {
// Calculs lourds
val result = performHeavyCalculation()
runOnUiThread {
updateUi(result)
}
}.start()
invalidate(Rect) et invalidate(int l, int t, int r, int b) permettent de limiter la zone de redessin. C'est crucial pour les performances : lorsqu'une seule partie d'une vue change (par exemple, mouvement du curseur, changement d'indicateur), il n'est pas nécessaire de redessiner la vue entière.
Le système transmet le rectangle dirty spécifié à onDraw() via canvas.clipBounds. À l'intérieur d'onDraw(), vous pouvez vérifier clipBounds et dessiner uniquement dans cette zone, bien qu'Android Canvas découpe automatiquement le dessin en dehors du rectangle dirty.
// Mise à jour partielle : zone du curseur uniquement
private val cursorRect = Rect()
fun moveCursorTo(newX: Int, newY: Int) {
// Invalider l'ancienne position
invalidate(cursorRect)
cursorRect.set(newX - 5, newY - 5,
newX + 5, newY + 5)
// Invalider la nouvelle position
invalidate(cursorRect)
}
Sans redessin partiel, chaque mouvement du curseur redessinerait la vue entière, ce qui pour un grand graphique signifie redessiner des milliers de pixels au lieu de quelques dizaines. invalidate(Rect) est une technique essentielle pour les éditeurs, les canevas de dessin et les composants animés.
Une erreur courante est d'appeler requestLayout() là où invalidate() suffirait, et vice versa. La différence est fondamentale : invalidate() n'affecte que la phase draw, tandis que requestLayout() déclenche un cycle complet measure → layout → draw.
| Aspect | invalidate() | requestLayout() |
|---|---|---|
| Phases du cycle | Draw uniquement | measure + layout + draw |
| Quand l'utiliser | Seul le rendu change (couleur, texte, graphiques) | La taille ou la position de la vue change |
| Performance | Léger — redessin uniquement | Lourd — recalcule la hiérarchie |
| Impact sur la hiérarchie | Uniquement la vue actuelle | Peut affecter les conteneurs parents |
Si vous changez le texte dans un TextView, invalidate() est suffisant car la taille de la vue ne change pas. Si le texte peut passer à une nouvelle ligne et augmenter la hauteur, requestLayout() est nécessaire. Android Lint aide à suivre ces erreurs via des règles de performance.
Les appels excessifs à invalidate() sont l'une des principales causes de mauvaise performance des vues personnalisées dans Android. Examinons les techniques d'optimisation.
Si les données sont mises à jour à haute fréquence (capteurs, animations, vidéo), n'appelez pas invalidate() à chaque changement. Utilisez ValueAnimator ou Choreographer.FrameCallback pour synchroniser avec la fréquence de rafraîchissement de l'écran. Cela garantit qu'invalidate() n'est pas appelé plus d'une fois par image.
Depuis l'API 14, Android prend en charge l'accélération matérielle via GPU. Si votre vue personnalisée utilise uniquement Canvas API (drawRect, drawCircle, drawPath), l'accélération fonctionne de manière transparente. Pour les opérations compatibles avec DisplayList, invalidate() est traité significativement plus rapidement.
// Utilisation de Choreographer pour la synchronisation Vsync
private val frameCallback = Choreographer.FrameCallback { frameTimeNanos ->
updateAnimation(frameTimeNanos)
invalidate()
Choreographer.getInstance().postFrameCallback(this)
}
fun startAnimation() {
Choreographer.getInstance().postFrameCallback(frameCallback)
}
Utilisez invalidate(Rect) pour des mises à jour ciblées, évitez d'appeler invalidate() depuis onDraw() (boucle infinie), et profilez toujours via GPU Profile Rendering sur un appareil. Cela montrera le temps de rendu exact de chaque image et aidera à identifier les zones problématiques.
Foire aux questions
Non, appeler invalidate() à l'intérieur d'onDraw() crée une boucle infinie de redessin : onDraw() appelle invalidate(), qui déclenche à nouveau onDraw(). Cela entraîne une utilisation à 100 % du CPU et des chutes d'images. Utilisez des animations via ValueAnimator ou Choreographer.
invalidate() fonctionne uniquement dans le thread UI et met à jour le drapeau dirty immédiatement. postInvalidate() envoie une demande via Handler au thread UI et peut être appelé depuis n'importe quel thread d'arrière-plan. Si vous êtes dans le thread UI — utilisez invalidate() pour une latence minimale.
Oui, en interne, la méthode setText() de TextView appelle invalidate() après la mise à jour du texte. Si le texte modifie les dimensions de la vue, requestLayout() est également appelé. Les développeurs n'ont pas besoin d'appeler manuellement invalidate() lorsqu'ils travaillent avec des widgets standard.
Chaque appel à invalidate() planifie un redessin dans le prochain Vsync (toutes les 16 ms). Si onDraw() prend plus de 16 ms, des chutes d'images se produisent. Optimisez onDraw() — mettez en cache les Bitmaps, évitez les allocations et utilisez l'accélération matérielle pour le rendu GPU.
Oui, après avoir modifié les propriétés de Paint (couleur, épaisseur, style), vous devez appeler invalidate(), car la View ne suit pas automatiquement les modifications des objets Paint. Le système ne sait pas que Paint a changé et n'appellera pas onDraw() sans une demande explicite.
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