invalidate() — este o metodă a clasei View în Android care marchează vederea ca necesitând redesenare. Apelarea invalidate() duce la redesenarea vederii în cel mai apropiat ciclu de actualizare a ecranului, făcându-l principalul mecanism de actualizare a stării vizuale a componentelor personalizate. Conform Android Developers Documentation (2025), invalidate() este utilizat în 90% din View-urile personalizate pentru sincronizarea modificărilor datelor cu afișarea pe ecran. Metoda funcționează asincron — doar setează un flag dirty și returnează controlul imediat.
Principalele
invalidate() — este o metodă a clasei android.view.View care informează sistemul Android că reprezentarea vizuală a vederii este învechită. După apelarea metodei, sistemul marchează vederea ca dirty și programează redesenarea acesteia în cel mai apropiat ciclu de actualizare a ecranului (de obicei 16 ms pentru 60 FPS).
Metoda invalidate() ia diferite forme: fără parametri (redesenare completă), cu parametrul Rect (parțială) și cu parametrii ltrb (left, top, right, bottom). Toate versiunile funcționează asincron și pot fi apelate din firul UI. Pentru apelarea din firele de fundal există postInvalidate().
Mecanismul de redesenare în Android se bazează pe ViewRootImpl — o componentă internă care leagă View Hierarchy de Surface pentru desenare. Când este apelat invalidate(), ViewRootImpl marchează zona vederii ca dirty și trimite o cerere de redesenare prin Choreographer — un serviciu de sistem care sincronizează desenarea cu frecvența de actualizare a ecranului.
Choreographer primește semnalul de la Vsync și lansează o triplă trecere: measure, layout, draw. Totuși, invalidate() afectează doar faza draw — fazele measure și layout nu sunt executate dacă nu a fost apelat requestLayout(). Aceasta este diferența cheie: invalidate() este mai ieftin decât requestLayout() deoarece nu recalculează 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() // Cerere de redescare
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
paint.color = Color.BLUE
paint.strokeWidth = 4f
paint.style = Paint.Style.STROKE
// Desenarea liniei graficului
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)
}
}
În acest exemplu, un View personalizat pentru desenarea graficului apelează invalidate() la actualizarea datelor. Sistemul redesenează doar acest View, fără a afecta celelalte elemente ale ierarhiei. onDraw() primește Canvas pentru desenarea liniilor prin Path.
Diferența principală între invalidate() și postInvalidate() este siguranța pentru fire. invalidate() trebuie apelat numai din firul UI (firul principal). postInvalidate() poate fi apelat din orice fir — prin Handler trimite o cerere de redesenare în firul UI.
| Caracteristică | invalidate() | postInvalidate() |
|---|---|---|
| Fir de apelare | Firul UI (firul principal) | Orice fir |
| Mecanism | Actualizare directă a dirty flag | Prin Handler.post() la firul UI |
| Latență | Minimă, în ciclul curent | Până la următorul ciclu al firului UI |
| Performanță | Ridicată | Supraîncărcare mică pe Handler |
| Recomandare | Pentru firul UI întotdeauna invalidate() | Numai pentru firele de fundal |
În practică, postInvalidate() este utilizat în scenarii de încărcare a datelor din rețea, procesare a rezultatelor senzorilor sau calcule de fundal. Dacă vă aflați în firul UI — utilizați întotdeauna invalidate() pentru o latență minimă.
// Apelat din firul UI
view.invalidate()
// Apelat din firul de fundal
Thread {
// Calcule grele
val result = performHeavyCalculation()
runOnUiThread {
updateUi(result)
}
}.start()
invalidate(Rect) și invalidate(int l, int t, int r, int b) permit limitarea zonei de redesenare. Acest lucru este critic pentru performanță: la actualizarea doar a unei părți a View (de exemplu, mișcarea cursorului, schimbarea indicatorului) nu are sens să redesenați întreaga vedere.
Sistemul transmite dreptunghiul dirty specificat în onDraw() prin canvas.clipBounds. În interiorul onDraw() se poate verifica clipBounds și desena doar în limitele acestei zone, deși Android Canvas decupează automat desenarea în afara dreptunghiului dirty.
// Actualizare parțială: doar zona cursorului
private val cursorRect = Rect()
fun moveCursorTo(newX: Int, newY: Int) {
// Invalidează poziția veche
invalidate(cursorRect)
cursorRect.set(newX - 5, newY - 5,
newX + 5, newY + 5)
// Invalidează poziția nouă
invalidate(cursorRect)
}
Fără redescarea parțială, fiecare mișcare a cursorului ar redesena întregul View, ceea ce pentru un grafic mare înseamnă redescarea a mii de pixeli în loc de câteva zeci. invalidate(Rect) — o tehnică obligatorie pentru editoare, pânze de desenare și componente animate.
Una dintre greșelile frecvente este apelarea requestLayout() acolo unde este suficient invalidate() și invers. Diferența este fundamentală: invalidate() afectează doar faza draw, iar requestLayout() lansează ciclul complet measure → layout → draw.
| Aspect | invalidate() | requestLayout() |
|---|---|---|
| Fazele ciclului | Doar draw | measure + layout + draw |
| Când să utilizați | Se schimbă doar desenarea (culoare, text, grafică) | Se schimbă dimensiunea sau poziția vederii |
| Performanță | Ușor — doar redescare | Greu — recalcularea ierarhiei |
| Impact asupra ierarhiei | Doar vederea curentă | Poate afecta containerele părinte |
Dacă modificați textul în TextView — este suficient invalidate(), deoarece dimensiunea vederii nu se schimbă. Dacă textul se poate muta pe o nouă linie și crește înălțimea — este necesar requestLayout(). Android Lint ajută la urmărirea unor astfel de erori prin reguli de performanță.
Apelurile excesive de invalidate() — una dintre principalele cauze ale performanței scăzute a View-urilor personalizate în Android. Să examinăm tehnicile de optimizare.
Dacă datele se actualizează cu frecvență ridicată (senzori, animație, video), nu apelați invalidate() la fiecare modificare. Utilizați ValueAnimator sau Choreographer.FrameCallback pentru sincronizarea cu frecvența de actualizare a ecranului. Aceasta garantează că invalidate() este apelat nu mai mult de o dată pe cadru.
Începând cu API 14, Android suportă accelerarea hardware prin GPU. Dacă View-ul dvs. personalizat utilizează doar Canvas API (drawRect, drawCircle, drawPath), accelerarea funcționează transparent. Pentru operațiuni compatibile cu DisplayList, invalidate() este procesat semnificativ mai rapid.
// Utilizarea Choreographer pentru sincronizarea Vsync
private val frameCallback = Choreographer.FrameCallback { frameTimeNanos ->
updateAnimation(frameTimeNanos)
invalidate()
Choreographer.getInstance().postFrameCallback(this)
}
fun startAnimation() {
Choreographer.getInstance().postFrameCallback(frameCallback)
}
Utilizați invalidate(Rect) pentru actualizări punctuale, evitați apelarea invalidate() din onDraw() (buclă infinită) și întotdeauna profilează prin GPU Profile Rendering pe dispozitiv. Aceasta va arăta timpul exact de desenare al fiecărui cadru și va ajuta la identificarea punctelor problematice.
Întrebări frecvente
Nu, apelarea invalidate() în interiorul onDraw() creează o buclă infinită de redescare: onDraw() apelează invalidate(), care lansează din nou onDraw(). Aceasta duce la încărcarea 100% a CPU și pierderea cadrelor. Utilizați animații prin ValueAnimator sau Choreographer.
invalidate() funcționează doar în firul UI și actualizează dirty flag imediat. postInvalidate() trimite o cerere prin Handler în firul UI și poate fi apelat din orice fir de fundal. Dacă sunteți în firul UI — utilizați invalidate() pentru o latență minimă.
Da, în interiorul TextView metoda setText() apelează invalidate() după actualizarea textului. Dacă textul a modificat dimensiunile vederii, suplimentar este apelat requestLayout(). Dezvoltatorul nu trebuie să apeleze manual invalidate() când lucrează cu widget-uri standard.
Fiecare apel invalidate() programează redescarea în următorul Vsync (la fiecare 16 ms). Dacă onDraw() se execută mai mult de 16 ms, are loc pierderea cadrelor. Optimizați onDraw() — faceți cache Bitmap, evitați alocările și utilizați Hardware Acceleration pentru desenarea GPU.
Da, după modificarea proprietăților Paint (culoare, grosime, stil) trebuie apelat invalidate(), deoarece View nu urmărește automat modificările obiectelor Paint. Sistemul nu știe că Paint s-a modificat și nu va apela onDraw() fără o cerere explicită.
Rezumat
Vom dezvolta o aplicație mobilă la cheie
IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.
Citiți și