invalidate() — je metoda třídy View v Androidu, která označuje zobrazení jako vyžadující překreslení. Volání invalidate() vede k překreslení zobrazení v nejbližším cyklu aktualizace obrazovky, což z něj činí hlavní mechanismus aktualizace vizuálního stavu vlastních komponent. Podle Android Developers Documentation (2025) se invalidate() používá v 90% vlastních View k synchronizaci změn dat se zobrazením na obrazovce. Metoda pracuje asynchronně — pouze nastaví příznak dirty a okamžitě vrátí řízení.
Hlavní body
invalidate() — je metoda třídy android.view.View, která informuje systém Android, že vizuální reprezentace zobrazení je zastaralá. Po volání metody systém označí zobrazení jako dirty a naplánuje jeho překreslení v nejbližším cyklu aktualizace obrazovky (obvykle 16 ms pro 60 FPS).
Metoda invalidate() přijímá různé formy: bez parametrů (úplné překreslení), s parametrem Rect (částečné) a s parametry ltrb (left, top, right, bottom). Všechny verze pracují asynchronně a mohou být volány z UI vlákna. Pro volání z vláken na pozadí existuje postInvalidate().
Mechanismus překreslení v Androidu je založen na ViewRootImpl — interní komponentě, která spojuje View Hierarchy s Surface pro kreslení. Když je zavoláno invalidate(), ViewRootImpl označí oblast zobrazení jako dirty a odešle požadavek na překreslení přes Choreographer — systémovou službu synchronizující kreslení s frekvencí aktualizace obrazovky.
Choreographer obdrží signál z Vsync a spustí trojitý průchod: measure, layout, draw. Nicméně invalidate() ovlivňuje pouze fázi draw — fáze measure a layout se neprovádějí, pokud nebylo zavoláno requestLayout(). To je klíčový rozdíl: invalidate() je levnější než requestLayout(), protože nepřepočítává geometrii.
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() // Požadavek na překreslení
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
paint.color = Color.BLUE
paint.strokeWidth = 4f
paint.style = Paint.Style.STROKE
// Kreslení čáry grafu
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)
}
}
V tomto příkladu vlastní View pro kreslení grafu volá invalidate() při aktualizaci dat. Systém překreslí pouze toto View, aniž by ovlivnil ostatní prvky hierarchie. onDraw() obdrží Canvas pro kreslení čar přes Path.
Hlavní rozdíl mezi invalidate() a postInvalidate() je v bezpečnosti vláken. invalidate() smí být voláno pouze z UI vlákna (hlavního vlákna). postInvalidate() lze volat z libovolného vlákna — přes Handler odesílá požadavek na překreslení do UI vlákna.
| Vlastnost | invalidate() | postInvalidate() |
|---|---|---|
| Vlákno volání | UI vlákno (hlavní vlákno) | Libovolné vlákno |
| Mechanismus | Přímá aktualizace dirty flag | Přes Handler.post() do UI vlákna |
| Latence | Minimální, v aktuálním cyklu | Do příštího cyklu UI vlákna |
| Výkon | Vysoký | Malá režie na Handleru |
| Doporučení | Pro UI vlákno vždy invalidate() | Pouze pro vlákna na pozadí |
V praxi se postInvalidate() používá ve scénářích načítání dat ze sítě, zpracování výsledků senzorů nebo výpočtů na pozadí. Pokud jste v UI vlákně — vždy používejte invalidate() pro minimální latenci.
// Voláno z UI vlákna
view.invalidate()
// Voláno z vlákna na pozadí
Thread {
// Těžké výpočty
val result = performHeavyCalculation()
runOnUiThread {
updateUi(result)
}
}.start()
invalidate(Rect) a invalidate(int l, int t, int r, int b) umožňují omezit oblast překreslení. To je kritické pro výkon: při aktualizaci pouze části View (např. pohyb kurzoru, změna indikátoru) nemá smysl překreslovat celé zobrazení.
Systém předá zadaný dirty obdélník do onDraw() přes canvas.clipBounds. Uvnitř onDraw() lze zkontrolovat clipBounds a kreslit pouze v mezích této oblasti, ačkoli Android Canvas automaticky ořezává kreslení mimo dirty obdélník.
// Částečná aktualizace: pouze oblast kurzoru
private val cursorRect = Rect()
fun moveCursorTo(newX: Int, newY: Int) {
// Zneplatnit starou pozici
invalidate(cursorRect)
cursorRect.set(newX - 5, newY - 5,
newX + 5, newY + 5)
// Zneplatnit novou pozici
invalidate(cursorRect)
}
Bez částečného překreslení by každý pohyb kurzoru překresloval celé View, což pro velký graf znamená překreslení tisíců pixelů místo několika desítek. invalidate(Rect) — povinná technika pro editory, kreslicí plátna a animované komponenty.
Jednou z častých chyb je volání requestLayout() tam, kde stačí invalidate(), a naopak. Rozdíl je zásadní: invalidate() ovlivňuje pouze fázi draw, zatímco requestLayout() spouští celý cyklus measure → layout → draw.
| Aspekt | invalidate() | requestLayout() |
|---|---|---|
| Fáze cyklu | Pouze draw | measure + layout + draw |
| Kdy použít | Mění se pouze kreslení (barva, text, grafika) | Mění se velikost nebo pozice zobrazení |
| Výkon | Lehké — pouze překreslení | Těžké — přepočet hierarchie |
| Dopad na hierarchii | Pouze aktuální zobrazení | Může ovlivnit nadřazené kontejnery |
Pokud měníte text v TextView — stačí invalidate(), protože velikost zobrazení se nemění. Pokud se text může přesunout na nový řádek a zvýšit výšku — je potřeba requestLayout(). Android Lint pomáhá sledovat takové chyby prostřednictvím pravidel výkonu.
Nadměrná volání invalidate() — jeden z hlavních důvodů nízkého výkonu vlastních View v Androidu. Podívejme se na techniky optimalizace.
Pokud se data aktualizují s vysokou frekvencí (senzory, animace, video), nevolejte invalidate() při každé změně. Používejte ValueAnimator nebo Choreographer.FrameCallback pro synchronizaci s frekvencí aktualizace obrazovky. To zaručuje, že invalidate() není voláno více než jednou za snímek.
Od API 14 Android podporuje hardwarovou akceleraci přes GPU. Pokud vaše vlastní View používá pouze Canvas API (drawRect, drawCircle, drawPath), akcelerace pracuje transparentně. Pro operace kompatibilní s DisplayList je invalidate() zpracováváno výrazně rychleji.
// Použití Choreographer pro synchronizaci Vsync
private val frameCallback = Choreographer.FrameCallback { frameTimeNanos ->
updateAnimation(frameTimeNanos)
invalidate()
Choreographer.getInstance().postFrameCallback(this)
}
fun startAnimation() {
Choreographer.getInstance().postFrameCallback(frameCallback)
}
Používejte invalidate(Rect) pro bodové aktualizace, vyhněte se volání invalidate() z onDraw() (nekonečná smyčka) a vždy profilujte přes GPU Profile Rendering na zařízení. To ukáže přesný čas kreslení každého snímku a pomůže identifikovat problematická místa.
Často kladené otázky
Ne, volání invalidate() uvnitř onDraw() vytváří nekonečnou smyčku překreslování: onDraw() volá invalidate(), který znovu spouští onDraw(). To vede k 100% zatížení CPU a ztrátě snímků. Používejte animace přes ValueAnimator nebo Choreographer.
invalidate() pracuje pouze v UI vlákně a aktualizuje dirty flag okamžitě. postInvalidate() odesílá požadavek přes Handler do UI vlákna a může být voláno z libovolného vlákna na pozadí. Pokud jste v UI vlákně — používejte invalidate() pro minimální latenci.
Ano, uvnitř TextView metoda setText() volá invalidate() po aktualizaci textu. Pokud text změnil rozměry zobrazení, dodatečně je voláno requestLayout(). Vývojář nemusí ručně volat invalidate() při práci se standardními widgety.
Každé volání invalidate() plánuje překreslení v příštím Vsync (každých 16 ms). Pokud onDraw() trvá déle než 16 ms, dochází ke ztrátě snímků. Optimalizujte onDraw() — kešujte Bitmap, vyhněte se alokacím a používejte Hardware Acceleration pro GPU kreslení.
Ano, po změně vlastností Paint (barva, tloušťka, styl) je třeba volat invalidate(), protože View nesleduje změny objektů Paint automaticky. Systém neví, že se Paint změnil, a bez explicitního požadavku nezavolá onDraw().
Shrnutí
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.
Přečtěte si také