invalidate() è un metodo della classe View in Android che marca una vista come bisognosa di essere ridisegnata. La chiamata a invalidate() provoca un ridisegno della vista nel prossimo ciclo di aggiornamento dello schermo, rendendolo il meccanismo principale per aggiornare lo stato visivo dei componenti personalizzati. Secondo la documentazione Android Developers (2025), invalidate() è utilizzato nel 90% delle View personalizzate per sincronizzare le modifiche dei dati con la visualizzazione sullo schermo. Il metodo funziona in modo asincrono — imposta solo il flag dirty e restituisce immediatamente il controllo.
Punti chiave
invalidate() è un metodo della classe android.view.View che comunica al sistema Android che la rappresentazione visiva di una vista è obsoleta. Dopo aver chiamato il metodo, il sistema marca la vista come dirty e pianifica il suo ridisegno nel prossimo ciclo di aggiornamento dello schermo (solitamente 16 ms per 60 FPS).
Il metodo invalidate() si presenta in varie forme: senza parametri (ridisegno completo), con un parametro Rect (parziale) e con parametri ltrb (left, top, right, bottom). Tutte le versioni funzionano in modo asincrono e devono essere chiamate dal thread UI. Per le chiamate da thread in background esiste postInvalidate().
Il meccanismo di ridisegno in Android si basa su ViewRootImpl — un componente interno che collega la gerarchia delle viste alla superficie di disegno. Quando viene chiamato invalidate(), ViewRootImpl marca l'area della vista come dirty e invia una richiesta di ridisegno tramite Choreographer — un servizio di sistema che sincronizza il disegno con la frequenza di aggiornamento dello schermo.
Choreographer riceve un segnale da Vsync e avvia un triplice passaggio: measure, layout, draw. Tuttavia, invalidate() interessa solo la fase draw — le fasi measure e layout non vengono eseguite a meno che non sia stato chiamato requestLayout(). Questa è una differenza chiave: invalidate() è più leggero di requestLayout() perché non ricalcola la geometria.
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() // Richiesta di ridisegno
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
paint.color = Color.BLUE
paint.strokeWidth = 4f
paint.style = Paint.Style.STROKE
// Disegnando linea del grafico
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)
}
}
In questo esempio, una View personalizzata per disegnare un grafico chiama invalidate() quando i dati vengono aggiornati. Il sistema ridisegna solo questa View senza influenzare gli altri elementi nella gerarchia. onDraw() riceve un Canvas per disegnare linee tramite Path.
La differenza principale tra invalidate() e postInvalidate() risiede nella sicurezza dei thread. invalidate() deve essere chiamato solo dal thread UI (thread principale). postInvalidate() può essere chiamato da qualsiasi thread — invia una richiesta di ridisegno al thread UI tramite Handler.
| Caratteristica | invalidate() | postInvalidate() |
|---|---|---|
| Thread di chiamata | Thread UI (thread principale) | Qualsiasi thread |
| Meccanismo | Aggiornamento diretto del flag dirty | Tramite Handler.post() al thread UI |
| Latenza | Minima, nel ciclo corrente | Fino al prossimo ciclo del thread UI |
| Prestazioni | Alte | Leggero overhead di Handler |
| Raccomandazione | Sempre invalidate() per il thread UI | Solo per thread in background |
In pratica, postInvalidate() viene utilizzato in scenari di caricamento dati di rete, elaborazione di risultati di sensori o calcoli in background. Se ti trovi nel thread UI — usa sempre invalidate() per una latenza minima.
// Chiamato dal thread UI
view.invalidate()
// Chiamato dal thread in background
Thread {
// Calcoli pesanti
val result = performHeavyCalculation()
runOnUiThread {
updateUi(result)
}
}.start()
invalidate(Rect) e invalidate(int l, int t, int r, int b) permettono di limitare l'area di ridisegno. Questo è fondamentale per le prestazioni: quando cambia solo una parte di una View (ad esempio, movimento del cursore, cambio indicatore), non è necessario ridisegnare l'intera vista.
Il sistema passa il rettangolo dirty specificato a onDraw() tramite canvas.clipBounds. All'interno di onDraw(), è possibile verificare clipBounds e disegnare solo all'interno di quell'area, sebbene Android Canvas ritagli automaticamente il disegno al di fuori del rettangolo dirty.
// Aggiornamento parziale: solo area cursore
private val cursorRect = Rect()
fun moveCursorTo(newX: Int, newY: Int) {
// Invalidare posizione vecchia
invalidate(cursorRect)
cursorRect.set(newX - 5, newY - 5,
newX + 5, newY + 5)
// Invalidare nuova posizione
invalidate(cursorRect)
}
Senza ridisegno parziale, ogni movimento del cursore ridisegnerebbe l'intera View, che per un grafico grande significa ridisegnare migliaia di pixel invece di poche decine. invalidate(Rect) è una tecnica essenziale per editor, canvas di disegno e componenti animati.
Un errore comune è chiamare requestLayout() dove basterebbe invalidate(), e viceversa. La differenza è fondamentale: invalidate() interessa solo la fase draw, mentre requestLayout() attiva un ciclo completo measure → layout → draw.
| Aspetto | invalidate() | requestLayout() |
|---|---|---|
| Fasi del ciclo | Solo draw | measure + layout + draw |
| Quando usarlo | Solo il rendering cambia (colore, testo, grafica) | La dimensione o la posizione della vista cambia |
| Prestazioni | Leggero — solo ridisegno | Pesante — ricalcola la gerarchia |
| Impatto sulla gerarchia | Solo la vista corrente | Può influenzare i contenitori padre |
Se modifichi il testo in un TextView, invalidate() è sufficiente poiché la dimensione della vista non cambia. Se il testo può andare a capo e aumentare l'altezza, è necessario requestLayout(). Android Lint aiuta a tracciare questi errori attraverso regole di prestazione.
Le chiamate eccessive a invalidate() sono una delle principali cause di scarse prestazioni delle View personalizzate in Android. Esaminiamo le tecniche di ottimizzazione.
Se i dati vengono aggiornati ad alta frequenza (sensori, animazioni, video), non chiamare invalidate() ad ogni modifica. Usa ValueAnimator o Choreographer.FrameCallback per sincronizzarti con la frequenza di aggiornamento dello schermo. Questo garantisce che invalidate() non venga chiamato più di una volta per fotogramma.
Dall'API 14, Android supporta l'accelerazione hardware tramite GPU. Se la tua View personalizzata utilizza solo Canvas API (drawRect, drawCircle, drawPath), l'accelerazione funziona in modo trasparente. Per le operazioni compatibili con DisplayList, invalidate() viene elaborato significativamente più velocemente.
// Usando Choreographer per sincronizzazione Vsync
private val frameCallback = Choreographer.FrameCallback { frameTimeNanos ->
updateAnimation(frameTimeNanos)
invalidate()
Choreographer.getInstance().postFrameCallback(this)
}
fun startAnimation() {
Choreographer.getInstance().postFrameCallback(frameCallback)
}
Usa invalidate(Rect) per aggiornamenti mirati, evita di chiamare invalidate() da onDraw() (ciclo infinito) e profilare sempre tramite GPU Profile Rendering su un dispositivo. Questo mostrerà il tempo esatto di rendering di ogni fotogramma e aiuterà a identificare le aree problematiche.
Domande frequenti
No, chiamare invalidate() all'interno di onDraw() crea un ciclo infinito di ridisegno: onDraw() chiama invalidate(), che a sua volta attiva onDraw(). Questo porta al 100% di utilizzo della CPU e caduta di fotogrammi. Usa animazioni tramite ValueAnimator o Choreographer.
invalidate() funziona solo nel thread UI e aggiorna il flag dirty immediatamente. postInvalidate() invia una richiesta tramite Handler al thread UI e può essere chiamato da qualsiasi thread in background. Se sei nel thread UI — usa invalidate() per una latenza minima.
Sì, internamente il metodo setText() di TextView chiama invalidate() dopo aver aggiornato il testo. Se il testo modifica le dimensioni della vista, viene chiamato anche requestLayout(). Gli sviluppatori non devono chiamare manualmente invalidate() quando lavorano con widget standard.
Ogni chiamata a invalidate() pianifica un ridisegno nel prossimo Vsync (ogni 16 ms). Se onDraw() richiede più di 16 ms, si verificano cadute di fotogrammi. Ottimizza onDraw() — memorizza nella cache i Bitmap, evita allocazioni e usa l'accelerazione hardware per il rendering GPU.
Sì, dopo aver modificato le proprietà di Paint (colore, spessore, stile), devi chiamare invalidate(), perché la View non tiene traccia automaticamente delle modifiche agli oggetti Paint. Il sistema non sa che Paint è cambiato e non chiamerà onDraw() senza una richiesta esplicita.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche