invalidate() — a View osztály metódusa Androidban, amely megjelöli a nézetet, mint újrarajzolást igénylőt. Az invalidate() hívása a nézet újrarajzolásához vezet a legközelebbi képernyőfrissítési ciklusban, így ez a fő mechanizmusa az egyedi komponensek vizuális állapotának frissítésének. Az Android Developers Documentation (2025) szerint az invalidate() az egyedi View-k 90%-ában használatos az adatváltozások és a képernyőn való megjelenítés szinkronizálására. A metódus aszinkron működik — csak beállítja a dirty flag-et és azonnal visszaadja a vezérlést.
Főbb pontok
invalidate() — az android.view.View osztály metódusa, amely értesíti az Android rendszert, hogy a nézet vizuális megjelenítése elavult. A metódus meghívása után a rendszer megjelöli a nézetet dirty jelzéssel, és ütemezi annak újrarajzolását a legközelebbi képernyőfrissítési ciklusban (általában 16 ms 60 FPS esetén).
Az invalidate() metódus különböző formákat ölthet: paraméter nélkül (teljes újrarajzolás), Rect paraméterrel (részleges) és ltrb paraméterekkel (left, top, right, bottom). Minden verzió aszinkron működik, és az UI szálból hívható. Háttérszálakból való híváshoz létezik a postInvalidate().
Az újrarajzolás mechanizmusa Androidban a ViewRootImpl -on alapul — egy belső komponensen, amely összeköti a View Hierarchy-t a Surface-szel a rajzoláshoz. Amikor invalidate() kerül meghívásra, a ViewRootImpl megjelöli a nézet területét dirty jelzéssel, és újrarajzolási kérelmet küld a Choreographer -en keresztül — egy rendszerszolgáltatáson, amely szinkronizálja a rajzolást a képernyő frissítési frekvenciájával.
A Choreographer jelet kap a Vsync-től, és elindít egy hármas áthaladást: measure, layout, draw. Azonban az invalidate() csak a draw fázisra hat — a measure és layout fázisok nem hajtódnak végre, hacsak nem hívták meg a requestLayout()-ot. Ez a kulcsfontosságú különbség: az invalidate() olcsóbb, mint a requestLayout(), mert nem számolja újra a geometriát.
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() // Újrarajzolási kérés
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
paint.color = Color.BLUE
paint.strokeWidth = 4f
paint.style = Paint.Style.STROKE
// Grafikonvonal rajzolása
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)
}
}
Ebben a példában egy egyedi View grafikon rajzolásához invalidate()-et hív meg az adatok frissítésekor. A rendszer csak ezt a View-t rajzolja újra, anélkül hogy a hierarchia többi elemére hatna. onDraw() megkapja a Canvas-t a vonalak Path-en keresztüli rajzolásához.
A fő különbség az invalidate() és a postInvalidate() között a szálbiztonság. invalidate() csak az UI szálból (fő szálból) hívható. postInvalidate() bármely szálból meghívható — Handleren keresztül küldi az újrarajzolási kérelmet az UI szálba.
| Jellemző | invalidate() | postInvalidate() |
|---|---|---|
| Hívás szál | UI szál (fő szál) | Bármely szál |
| Mechanizmus | Dirty flag közvetlen frissítése | Handler.post() segítségével az UI szálba |
| Késleltetés | Minimális, az aktuális ciklusban | A következő UI szál ciklusig |
| Teljesítmény | Magas | Kis többletterhelés a Handleren |
| Ajánlás | UI szálhoz mindig invalidate() | Csak háttérszálakhoz |
A gyakorlatban a postInvalidate() hálózati adatbetöltés, érzékelőeredmények feldolgozása vagy háttérszámítások esetén használatos. Ha az UI szálban van — mindig invalidate()-et használjon a minimális késleltetésért.
// UI szálból hívva
view.invalidate()
// Háttérszálból hívva
Thread {
// Nehéz számítások
val result = performHeavyCalculation()
runOnUiThread {
updateUi(result)
}
}.start()
invalidate(Rect) és invalidate(int l, int t, int r, int b) lehetővé teszi az újrarajzolási terület korlátozását. Ez kritikus a teljesítmény szempontjából: ha csak a View egy részét frissítjük (pl. kurzor mozgatása, jelző megváltoztatása), nincs értelme az egész nézetet újrarajzolni.
A rendszer átadja a megadott dirty téglalapot az onDraw()-nak a canvas.clipBounds segítségével. Az onDraw()-on belül ellenőrizhető a clipBounds, és csak ezen a területen belül lehet rajzolni, bár az Android Canvas automatikusan levágja a rajzolást a dirty téglalapon kívül.
// Részleges frissítés: csak kurzor terület
private val cursorRect = Rect()
fun moveCursorTo(newX: Int, newY: Int) {
// Régi pozíció érvénytelenítése
invalidate(cursorRect)
cursorRect.set(newX - 5, newY - 5,
newX + 5, newY + 5)
// Új pozíció érvénytelenítése
invalidate(cursorRect)
}
Részleges újrarajzolás nélkül minden kurzormozgás az egész View-t újrarajzolná, ami egy nagy grafikon esetében több ezer pixel újrarajzolását jelentené néhány tíz helyett. invalidate(Rect) — kötelező technika szerkesztőkhöz, rajzoló vásznakhoz és animált komponensekhez.
Az egyik gyakori hiba, hogy requestLayout()-ot hívnak ott, ahol invalidate() is elég, és fordítva. A különbség alapvető: invalidate() csak a draw fázisra hat, míg a requestLayout() elindítja a teljes measure → layout → draw ciklust.
| Szempont | invalidate() | requestLayout() |
|---|---|---|
| Ciklus fázisai | Csak draw | measure + layout + draw |
| Mikor használjuk | Csak a rajzolás változik (szín, szöveg, grafika) | A nézet mérete vagy pozíciója változik |
| Teljesítmény | Könnyű — csak újrarajzolás | Nehéz — hierarchia újraszámolása |
| Hatás a hierarchiára | Csak az aktuális nézet | Befolyásolhatja a szülő tárolókat |
Ha szöveget változtat a TextView-ban — elegendő az invalidate(), mivel a nézet mérete nem változik. Ha a szöveg új sorba kerülhet és növelheti a magasságot — requestLayout() szükséges. Android Lint segít az ilyen hibák követésében teljesítményszabályokon keresztül.
A túlzott invalidate() hívások — az egyedi View-k alacsony teljesítményének egyik fő oka Androidban. Tekintsük át az optimalizálási technikákat.
Ha az adatok nagy gyakorisággal frissülnek (érzékelők, animáció, videó), ne hívja meg az invalidate()-et minden változáskor. Használja a ValueAnimator-t vagy a Choreographer.FrameCallback-et a képernyő frissítési frekvenciájával való szinkronizáláshoz. Ez garantálja, hogy az invalidate() nem hívódik meg többször, mint egyszer kockánként.
Az API 14-től kezdve az Android támogatja a hardveres gyorsítást GPU-n keresztül. Ha az egyedi View csak Canvas API-t (drawRect, drawCircle, drawPath) használ, a gyorsítás átláthatóan működik. DisplayList-kompatibilis műveletekhez az invalidate() jelentősen gyorsabban kerül feldolgozásra.
// Choreographer használata Vsync szinkronizáláshoz
private val frameCallback = Choreographer.FrameCallback { frameTimeNanos ->
updateAnimation(frameTimeNanos)
invalidate()
Choreographer.getInstance().postFrameCallback(this)
}
fun startAnimation() {
Choreographer.getInstance().postFrameCallback(frameCallback)
}
Használja az invalidate(Rect)-et pontszerű frissítésekhez, kerülje az invalidate() hívását onDraw()-ból (végtelen ciklus), és mindig profilozzon GPU Profile Rendering segítségével az eszközön. Ez megmutatja az egyes kockák rajzolási idejét, és segít azonosítani a problémás helyeket.
Gyakran Ismételt Kérdések
Nem, az invalidate() meghívása az onDraw()-on belül végtelen újrarajzolási ciklust hoz létre: az onDraw() meghívja az invalidate()-et, ami újra elindítja az onDraw()-ot. Ez a CPU 100%-os terheléséhez és kockavesztéshez vezet. Használjon animációkat a ValueAnimator vagy Choreographer segítségével.
invalidate() csak az UI szálban működik, és azonnal frissíti a dirty flag-et. postInvalidate() a Handleren keresztül küldi a kérelmet az UI szálba, és bármely háttérszálból hívható. Ha az UI szálban van — használja az invalidate()-et a minimális késleltetésért.
Igen, a TextView-n belül a setText() metódus meghívja az invalidate()-et a szöveg frissítése után. Ha a szöveg megváltoztatta a nézet méreteit, kiegészítőleg requestLayout() kerül meghívásra. A fejlesztőnek nem kell manuálisan meghívnia az invalidate()-et a szabványos widgetek használatakor.
Minden invalidate() hívás a következő Vsync-ben (16 ms-ként) ütemezi az újrarajzolást. Ha az onDraw() tovább tart 16 ms-nál, kockavesztés következik be. Optimalizálja az onDraw()-ot — gyorsítótárazza a Bitmap-ot, kerülje az allokációkat, és használja a Hardware Acceleration-t a GPU rajzoláshoz.
Igen, a Paint tulajdonságainak (szín, vastagság, stílus) megváltoztatása után meg kell hívni az invalidate()-et, mert a View nem követi automatikusan a Paint objektumok változásait. A rendszer nem tudja, hogy a Paint megváltozott, és nem hívja meg az onDraw()-ot explicit kérés nélkül.
Összefoglaló
Kulcsrakész mobilalkalmazást fejlesztünk
Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.
Olvassa el is