invalidate() — је метода класе View у Android-у која означава приказ као да захтева поновно цртање. Позив invalidate() доводи до прецртавања приказа у најближем циклусу ажурирања екрана, што га чини главним механизмом за ажурирање визуелног стања прилагођених компонената. Према Android Developers Documentation (2025), invalidate() се користи у 90% прилагођених View-ова за синхронизацију промена података са приказом на екрану. Метода ради асинхроно — само поставља dirty заставицу и одмах враћа контролу.
Главно
invalidate() — је метода класе android.view.View која обавештава Android систем да је визуелни приказ приказа застарео. Након позива методе, систем означава приказ као dirty и планира његово прецртавање у најближем циклусу ажурирања екрана (обично 16 ms за 60 FPS).
Метода invalidate() прима различите облике: без параметара (потпуно прецртавање), са параметром Rect (делимично) и са параметрима ltrb (left, top, right, bottom). Све верзије раде асинхроно и могу се позивати из UI нити. За позивање из позадинских нити постоји postInvalidate().
Механизам прецртавања у Android-у се заснива на ViewRootImpl — унутрашњој компоненти која повезује View Hierarchy са Surface-ом за цртање. Када се позове invalidate(), ViewRootImpl означава област приказа као dirty и шаље захтев за прецртавање кроз Choreographer — системски сервис који синхронизује цртање са учесталошћу ажурирања екрана.
Choreographer прима сигнал од Vsync-а и покреће троструки пролаз: measure, layout, draw. Међутим, invalidate() утиче само на фазу draw — фазе measure и layout се не извршавају ако није позван requestLayout(). Ово је кључна разлика: invalidate() је јефтинији од requestLayout()-а јер не прерачунава геометрију.
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() // Захтев за прецртавање
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
paint.color = Color.BLUE
paint.strokeWidth = 4f
paint.style = Paint.Style.STROKE
// Цртање линије графикона
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)
}
}
У овом примеру, прилагођени View за цртање графикона позива invalidate() при ажурирању података. Систем прецртава само овај View, не утичући на остале елементе хијерархије. onDraw() добија Canvas за цртање линија кроз Path.
Главна разлика између invalidate()-а и postInvalidate()-а је у безбедности нити. invalidate() се сме позивати само из UI нити (главне нити). postInvalidate() се може позвати из било које нити — кроз Handler шаље захтев за прецртавање у UI нит.
| Карактеристика | invalidate() | postInvalidate() |
|---|---|---|
| Нит позива | UI нит (главна нит) | Било која нит |
| Механизам | Директно ажурирање dirty заставице | Кроз Handler.post() ка UI нити |
| Касњење | Минимално, у тренутном циклусу | До следећег циклуса UI нити |
| Перформансе | Високе | Мали overhead на Handler-у |
| Препорука | За UI нит увек invalidate() | Само за позадинске нити |
У пракси, postInvalidate() се користи у сценаријима учитавања података из мреже, обраде резултата сензора или позадинских израчунавања. Ако се налазите у UI нити — увек користите invalidate() за минимално кашњење.
// Позвано из UI нити
view.invalidate()
// Позвано из позадинске нити
Thread {
// Тешка израчунавања
val result = performHeavyCalculation()
runOnUiThread {
updateUi(result)
}
}.start()
invalidate(Rect) и invalidate(int l, int t, int r, int b) омогућавају ограничавање области прецртавања. Ово је критично за перформансе: при ажурирању само дела View-а (нпр. померање курсора, промена индикатора) нема смисла прецртавати цео приказ.
Систем прослеђује наведени dirty правоугаоник у onDraw() кроз canvas.clipBounds. Унутар onDraw()-а може се проверити clipBounds и цртати само у границама ове области, иако Android Canvas аутоматски ограничава цртање изван dirty правоугаоника.
// Делимично ажурирање: само област курсора
private val cursorRect = Rect()
fun moveCursorTo(newX: Int, newY: Int) {
// Поништи стару позицију
invalidate(cursorRect)
cursorRect.set(newX - 5, newY - 5,
newX + 5, newY + 5)
// Поништи нову позицију
invalidate(cursorRect)
}
Без делимичног прецртавања, сваки покрет курсора би прецртавао цео View, што за велики графикон значи прецртавање хиљада пиксела уместо неколико десетина. invalidate(Rect) — обавезна техника за едиторе, платна за цртање и анимиране компоненте.
Једна од честих грешака је позивање requestLayout()-а тамо где је довољан invalidate(), и обрнуто. Разлика је фундаментална: invalidate() утиче само на фазу draw, док requestLayout() покреће потпуни циклус measure → layout → draw.
| Аспект | invalidate() | requestLayout() |
|---|---|---|
| Фазе циклуса | Само draw | measure + layout + draw |
| Када користити | Мења се само цртање (боја, текст, графика) | Мења се величина или положај приказа |
| Перформансе | Лагано — само прецртавање | Тешко — прерачунавање хијерархије |
| Утицај на хијерархију | Само тренутни приказ | Може утицати на родитељске контејнере |
Ако мењате текст у TextView-у — довољан је invalidate(), јер се величина приказа не мења. Ако текст може да пређе у нови ред и повећа висину — потребан је requestLayout(). Android Lint помаже у праћењу оваквих грешака кроз правила перформанси.
Прекомерни позиви invalidate()-а — један од главних узрока ниских перформанси прилагођених View-ова у Android-у. Размотримо технике оптимизације.
Ако се подаци ажурирају високом учесталошћу (сензори, анимација, видео), не позивајте invalidate() при свакој промени. Користите ValueAnimator или Choreographer.FrameCallback за синхронизацију са учесталошћу ажурирања екрана. Ово гарантује да се invalidate() позива не више од једном по кадру.
Од API 14, Android подржава хардверско убрзање кроз GPU. Ако ваш прилагођени View користи само Canvas API (drawRect, drawCircle, drawPath), убрзање ради транспарентно. За операције компатибилне са DisplayList-ом, invalidate() се обрађује значајно брже.
// Коришћење Choreographer-а за Vsync синхронизацију
private val frameCallback = Choreographer.FrameCallback { frameTimeNanos ->
updateAnimation(frameTimeNanos)
invalidate()
Choreographer.getInstance().postFrameCallback(this)
}
fun startAnimation() {
Choreographer.getInstance().postFrameCallback(frameCallback)
}
Користите invalidate(Rect) за тачкаста ажурирања, избегавајте позивање invalidate()-а из onDraw()-а (бесконачна петља) и увек профилишите кроз GPU Profile Rendering на уређају. Ово ће показати тачно време цртања сваког кадра и помоћи у идентификацији проблематичних места.
Често постављана питања
Не, позив invalidate()-а унутар onDraw()-а ствара бесконачни циклус прецртавања: onDraw() позива invalidate(), који поново покреће onDraw(). Ово доводи до 100% оптерећења CPU-а и пада броја кадрова. Користите анимације кроз ValueAnimator или Choreographer.
invalidate() ради само у UI нити и ажурира dirty заставицу одмах. postInvalidate() шаље захтев кроз Handler у UI нит и може се позвати из било које позадинске нити. Ако сте у UI нити — користите invalidate() за минимално кашњење.
Да, унутар TextView-а метода setText() позива invalidate() након ажурирања текста. Ако је текст променио димензије приказа, додатно се позива requestLayout(). Програмер не мора ручно да позива invalidate() при раду са стандардним виџетима.
Сваки позив invalidate()-а планира прецртавање у следећем Vsync-у (сваких 16 ms). Ако onDraw() траје дуже од 16 ms, долази до пада кадрова. Оптимизујте onDraw() — кеширајте Bitmap, избегавајте алокације и користите Hardware Acceleration за GPU цртање.
Да, након промене својстава Paint-а (боја, дебљина, стил) потребно је позвати invalidate(), јер View не прати промене Paint објеката аутоматски. Систем не зна да се Paint променио и неће позвати onDraw() без експлицитног захтева.
Резиме
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође