invalidate() — co to jest, mechanizm przerysowania i invalidate(Rect)

Autor: IT Sectr Opublikowano: 2026-07-20 Czas czytania: 7 min

invalidate() — to metoda klasy View w Androidzie, która oznacza widok jako wymagający ponownego przerysowania. Wywołanie invalidate() powoduje przerysowanie widoku w najbliższym cyklu odświeżania ekranu, co czyni go głównym mechanizmem aktualizacji stanu wizualnego niestandardowych komponentów. Według Android Developers Documentation (2025), invalidate() jest używane w 90% niestandardowych View do synchronizacji zmian danych z wyświetlaniem na ekranie. Metoda działa asynchronicznie — jedynie ustawia flagę dirty i natychmiast zwraca sterowanie.

Najważniejsze

  • invalidate() — asynchroniczne żądanie przerysowania View w Androidzie, działa poprzez mechanizm dirty flag
  • postInvalidate() — wersja invalidate() do wywołania z wątku tła, bezpieczna wątkowo
  • invalidate(Rect) — częściowe przerysowanie tylko określonego obszaru dla optymalizacji wydajności
  • onDraw() — metoda wywoływana przez system po invalidate(), analogiczna do draw(_:) w iOS
  • invalidate() vs requestLayout() — invalidate() przerysowuje zawartość, requestLayout() przelicza geometrię

Czym jest invalidate() w Androidzie

invalidate() — to metoda klasy android.view.View, która informuje system Android, że wizualna reprezentacja widoku jest nieaktualna. Po wywołaniu metody system oznacza widok jako dirty i planuje jego przerysowanie w najbliższym cyklu odświeżania ekranu (zwykle 16 ms dla 60 FPS).

Metoda invalidate() przyjmuje różne formy: bez parametrów (pełne przerysowanie), z parametrem Rect (częściowe) oraz z parametrami ltrb (left, top, right, bottom). Wszystkie wersje działają asynchronicznie i mogą być wywoływane z wątku UI. Do wywołania z wątków tła istnieje postInvalidate().

Jak działa przerysowanie przez invalidate()

Mechanizm przerysowania w Androidzie opiera się na ViewRootImpl — wewnętrznym komponencie, który łączy View Hierarchy z Surface do rysowania. Gdy wywoływane jest invalidate(), ViewRootImpl oznacza obszar widoku jako dirty i wysyła żądanie przerysowania przez Choreographer — usługę systemową synchronizującą rysowanie z częstotliwością odświeżania ekranu.

Cykl przerysowania

Choreographer otrzymuje sygnał od Vsync i uruchamia potrójny przebieg: measure, layout, draw. Jednak invalidate() dotyczy tylko fazy draw — fazy measure i layout nie są wykonywane, jeśli nie został wywołany requestLayout(). To kluczowa różnica: invalidate() jest tańsze niż requestLayout(), ponieważ nie przelicza geometrii.

kotlin
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() // Żądanie przerysowania
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        paint.color = Color.BLUE
        paint.strokeWidth = 4f
        paint.style = Paint.Style.STROKE

        // Rysowanie linii wykresu
        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)
    }
}

W tym przykładzie niestandardowy View do rysowania wykresu wywołuje invalidate() przy aktualizacji danych. System przerysowuje tylko ten View, nie wpływając na pozostałe elementy hierarchii. onDraw() otrzymuje Canvas do rysowania linii przez Path.

invalidate() vs postInvalidate()

Główna różnica między invalidate() a postInvalidate() polega na bezpieczeństwie wątkowym. invalidate() może być wywoływane tylko z wątku UI (main thread). postInvalidate() można wywoływać z dowolnego wątku — przez Handler wysyła on żądanie przerysowania do wątku UI.

Cechainvalidate()postInvalidate()
Wątek wywołaniaUI-Thread (główny wątek)Dowolny wątek
MechanizmBezpośrednia aktualizacja dirty flagPrzez Handler.post() do wątku UI
OpóźnienieMinimalne, w bieżącym cykluDo następnego cyklu wątku UI
WydajnośćWysokaNiewielki narzut na Handler
ZalecenieDla wątku UI zawsze invalidate()Tylko dla wątków tła

W praktyce postInvalidate() jest używane w scenariuszach ładowania danych z sieci, przetwarzania wyników czujników lub obliczeń w tle. Jeśli znajdujesz się w wątku UI — zawsze używaj invalidate() dla minimalnego opóźnienia.

kotlin
    // Wywołane z wątku UI
view.invalidate()

    // Wywołane z wątku tła
Thread {
    // Ciężkie obliczenia
    val result = performHeavyCalculation()
    runOnUiThread {
        updateUi(result)
    }
}.start()

Częściowe przerysowanie przez invalidate(Rect)

invalidate(Rect) i invalidate(int l, int t, int r, int b) pozwalają ograniczyć obszar przerysowania. Jest to krytyczne dla wydajności: przy aktualizacji tylko części View (np. ruch kursora, zmiana wskaźnika) nie ma sensu przerysowywać całego widoku.

System przekazuje określony prostokąt dirty do onDraw() przez canvas.clipBounds. Wewnątrz onDraw() można sprawdzić clipBounds i rysować tylko w obrębie tego obszaru, chociaż Android Canvas automatycznie przycina rysowanie poza prostokątem dirty.

kotlin
    // Aktualizacja częściowa: tylko obszar kursora
private val cursorRect = Rect()

fun moveCursorTo(newX: Int, newY: Int) {
    // Unieważnij starą pozycję
    invalidate(cursorRect)

    cursorRect.set(newX - 5, newY - 5,
                  newX + 5, newY + 5)

    // Unieważnij nową pozycję
    invalidate(cursorRect)
}

Bez częściowego przerysowania każdy ruch kursora przerysowywałby cały View, co dla dużego wykresu oznacza przerysowanie tysięcy pikseli zamiast kilkudziesięciu. invalidate(Rect) — obowiązkowa technika dla edytorów, canvasów rysowania i animowanych komponentów.

invalidate() vs requestLayout(): jaka jest różnica

Jednym z częstych błędów jest wywołanie requestLayout() tam, gdzie wystarczy invalidate(), i odwrotnie. Różnica jest fundamentalna: invalidate() dotyczy tylko fazy draw, a requestLayout() uruchamia pełny cykl measure → layout → draw.

Aspektinvalidate()requestLayout()
Fazy cykluTylko drawmeasure + layout + draw
Kiedy używaćZmienia się tylko rysowanie (kolor, tekst, grafika)Zmienia się rozmiar lub pozycja widoku
WydajnośćLekkie — tylko przerysowanieCiężkie — przeliczanie hierarchii
Wpływ na hierarchięTylko bieżący widokMoże wpłynąć na kontenery nadrzędne

Jeśli zmieniasz tekst w TextView — wystarczy invalidate(), ponieważ rozmiar widoku się nie zmienia. Jeśli tekst może przejść do nowej linii i zwiększyć wysokość — potrzebny jest requestLayout(). Android Lint pomaga śledzić takie błędy poprzez reguły wydajności.

Optymalizacja wydajności invalidate()

Nadmierne wywołania invalidate() — jedna z głównych przyczyn niskiej wydajności niestandardowych View w Androidzie. Rozważmy techniki optymalizacji.

Minimalizuj częstotliwość wywołań

Jeśli dane aktualizują się z wysoką częstotliwością (czujniki, animacja, wideo), nie wywołuj invalidate() przy każdej zmianie. Używaj ValueAnimator lub Choreographer.FrameCallback do synchronizacji z częstotliwością odświeżania ekranu. Gwarantuje to, że invalidate() jest wywoływane nie częściej niż raz na klatkę.

Używaj Hardware Acceleration

Od API 14 Android obsługuje przyspieszenie sprzętowe przez GPU. Jeśli Twój niestandardowy View używa tylko Canvas API (drawRect, drawCircle, drawPath), przyspieszenie działa przezroczysto. Dla operacji zgodnych z DisplayList, invalidate() jest przetwarzane znacznie szybciej.

kotlin
// Używanie Choreographer do synchronizacji Vsync
private val frameCallback = Choreographer.FrameCallback { frameTimeNanos ->
    updateAnimation(frameTimeNanos)
    invalidate()
    Choreographer.getInstance().postFrameCallback(this)
}

fun startAnimation() {
    Choreographer.getInstance().postFrameCallback(frameCallback)
}

Używaj invalidate(Rect) do punktowych aktualizacji, unikaj wywoływania invalidate() z onDraw() (nieskończona pętla) i zawsze profiluj przez GPU Profile Rendering na urządzeniu. Pokaże to dokładny czas rysowania każdej klatki i pomoże zidentyfikować problematyczne miejsca.

Często zadawane pytania

Czy można wywoływać invalidate() z onDraw()?

Nie, wywołanie invalidate() wewnątrz onDraw() tworzy nieskończoną pętlę przerysowania: onDraw() wywołuje invalidate(), który ponownie uruchamia onDraw(). Prowadzi to do 100% obciążenia CPU i spadku klatek. Używaj animacji przez ValueAnimator lub Choreographer.

Czym różni się invalidate() od postInvalidate()?

invalidate() działa tylko w wątku UI i aktualizuje dirty flag natychmiast. postInvalidate() wysyła żądanie przez Handler do wątku UI i może być wywoływane z dowolnego wątku tła. Jeśli jesteś w wątku UI — używaj invalidate() dla minimalnego opóźnienia.

Czy setText() w TextView automatycznie wywołuje invalidate()?

Tak, wewnątrz TextView metoda setText() wywołuje invalidate() po aktualizacji tekstu. Jeśli tekst zmienił rozmiary widoku, dodatkowo wywoływany jest requestLayout(). Deweloper nie musi ręcznie wywoływać invalidate() podczas pracy ze standardowymi widgetami.

Jak invalidate() wpływa na wydajność przy 60 FPS?

Każde wywołanie invalidate() planuje przerysowanie w następnym Vsync (co 16 ms). Jeśli onDraw() wykonuje się dłużej niż 16 ms, następuje spadek klatek. Optymalizuj onDraw() — cache'uj Bitmap, unikaj alokacji i używaj Hardware Acceleration do rysowania przez GPU.

Czy trzeba wywoływać invalidate() po zmianie właściwości Paint?

Tak, po zmianie właściwości Paint (kolor, grubość, styl) należy wywołać invalidate(), ponieważ View nie śledzi zmian obiektów Paint automatycznie. System nie wie, że Paint się zmienił, i nie wywoła onDraw() bez jawnego żądania.

Podsumowanie

  • invalidate() — główny mechanizm żądania przerysowania View w Androidzie, działa asynchronicznie przez dirty flag
  • postInvalidate() — bezpieczna wątkowo wersja do wywołania z wątków tła, używa Handler
  • invalidate(Rect) — częściowe przerysowanie tylko określonego obszaru, krytyczne dla wydajności przy punktowych zmianach
  • requestLayout() — uruchamia pełny cykl measure + layout + draw, znacznie cięższy niż invalidate()
  • Choreographer — systemowa usługa synchronizacji z Vsync, zalecana do animacji z invalidate()
  • Hardware Acceleration — przyspieszenie GPU dostępne od API 14, przyspiesza przetwarzanie invalidate() dla Canvas API
  • GPU Profile Rendering — narzędzie profilowania do pomiaru czasu rysowania i identyfikacji wolnych onDraw()

Opracujemy aplikację mobilną pod klucz

IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.

Omów projekt

Przeczytaj również