TextWatcher: co to jest, interfejs TextWatcher i implementacja w Androidzie

Autor: IT Sectr Opublikowano: 2026-07-08 Czas czytania: 6 min

TextWatcher to interfejs Androida, który umożliwia śledzenie zmian tekstu w EditText i innych TextView w czasie rzeczywistym. Deweloper otrzymuje powiadomienia na trzech etapach: przed zmianą, w trakcie i po zmianie zawartości tekstowej. Według Android Developers, 2026, TextWatcher jest używany w większości aplikacji do walidacji wprowadzania, liczenia znaków, implementacji wyszukiwania z autouzupełnianiem i dynamicznego formatowania tekstu. Interfejs jest niezastąpiony w formularzach, gdzie wymagana jest natychmiastowa reakcja na każde naciśnięcie klawisza.

Najważniejsze

  • TextWatcher — wbudowany interfejs Android SDK do nasłuchiwania zmian tekstu w TextView i EditText.
  • Interfejs zawiera trzy metody: beforeTextChanged, onTextChanged i afterTextChanged, każda odpowiada za swój etap zmiany.
  • Metoda afterTextChanged jest najwygodniejsza do walidacji pola po zakończeniu wprowadzania przez użytkownika.
  • Wywołanie rekurencyjne — częsty błąd: zmiana tekstu wewnątrz TextWatcher prowadzi do nieskończonej pętli.
  • TextWatcher jest używany w polach wyszukiwania, walidacji formularzy, liczeniu znaków i autoformatowaniu numeru telefonu.

Co to jest TextWatcher i do czego służy?

TextWatcher to interfejs z pakietu android.text, który powiadamia aplikację o zmianach tekstu w obiektach Editable. Przy każdym wprowadzeniu, usunięciu lub zastąpieniu znaku TextWatcher sekwencyjnie wywołuje trzy metody, przekazując informacje o położeniu zmian. Pozwala to deweloperowi reagować na działania użytkownika natychmiastowo — bez dodatkowych przycisków lub wyzwalaczy.

Główne scenariusze użycia obejmują walidację pól w czasie rzeczywistym: sprawdzanie emaila przy wprowadzaniu każdego znaku, liczenie pozostałych znaków w polu z ograniczeniem długości, implementację wyszukiwania z opóźnionym wysyłaniem zapytania przez debounce. TextWatcher jest również używany do formatowania wprowadzania — na przykład automatyczne wstawianie spacji w numerze telefonu lub dodawanie maski dla daty.

Według Android Developers, TextWatcher występuje w 70% aplikacji pracujących z formularzami. Biblioteki takie jak Material Design Components i TextInputEditText używają TextWatcher wewnętrznie do zarządzania stanem błędu i wyświetlania liczników. Zrozumienie działania tego interfejsu jest niezbędne dla każdego dewelopera Androida.

Jak działa interfejs TextWatcher

TextWatcher podłącza się do dowolnego obiektu TextView lub EditText przez metodę addTextChangedListener. Gdy użytkownik wprowadza lub usuwa znak, Android wywołuje najpierw beforeTextChanged, następnie onTextChanged i na końcu afterTextChanged. W parametrach każdej metody przekazywane są dane o zmienianym zakresie: pozycja początkowa, liczba usuniętych znaków i liczba dodanych znaków.

Warto zrozumieć, że po wywołaniu afterTextChanged obiekt Editable zawiera już aktualną wartość. Dlatego właśnie w afterTextChanged wygodnie jest sprawdzać końcowy tekst pola. Do tego momentu dane nie są jeszcze w pełni zaktualizowane. Deweloperzy często mylą przeznaczenie metod i używają onTextChanged do końcowej walidacji, chociaż właściwym wyborem jest afterTextChanged.

Specyfika wywoływania metod

Przy każdej inseracji, zastąpieniu lub usunięciu znaku łańcuch wywołań jest gwarantowanie wykonywany w całości. Jednak jeśli wewnątrz afterTextChanged zmieni się tekst (przez clear, append, insert), TextWatcher zadziała rekurencyjnie. To najczęstsza przyczyna StackOverflowError w formularzach Androida. Aby zapobiec rekurencji, stosuje się flagę blokującą.

Trzy metody TextWatcher: beforeTextChanged, onTextChanged, afterTextChanged

Każda z trzech metod pełni swoją rolę w cyklu życia zmiany tekstu. Metoda beforeTextChanged(CharSequence s, int start, int count, int after) jest wywoływana przed zastosowaniem zmian. Przekazuje bieżący stan łańcucha, pozycję początku zmiany, liczbę usuwanych znaków i liczbę dodawanych. Tutaj można zapisać poprzednią wartość lub sprawdzić warunki przed modyfikacją.

Metoda onTextChanged jest wywoływana w trakcie zmiany, gdy znaki zostały już usunięte, ale nowe jeszcze nie są wstawione. Parametry: tekst po usunięciu, pozycja początkowa, liczba usuniętych znaków i liczba dodawanych. Ta metoda jest wygodna do animacji lub logowania, ale nie do pracy z aktualnym końcowym tekstem — nie jest jeszcze skompletowany.

Metoda afterTextChanged jest najbardziej pożądana. Otrzymuje obiekt Editable i jest wywoływana po pełnym zastosowaniu zmian. W tej metodzie można odczytać końcową wartość pola, wykonać walidację, zaktualizować UI i zmienić tekst (ostrożnie ze względu na rekurencję).

Przykład implementacji TextWatcher do liczenia znaków

Praktyczny przykład — licznik znaków dla pola wprowadzania, który aktualizuje się przy każdej zmianie tekstu. Taki element często występuje w formularzach kontaktowych, postach i wiadomościach z ograniczeniem długości. Implementacja przez TextWatcher zajmuje kilka linii i nie wymaga bibliotek zewnętrznych.

kotlin
val editText = findViewById<EditText>(R.id.edit_text)
val counterText = findViewById<TextView>(R.id.counter)

editText.addTextChangedListener(object : TextWatcher {
    override fun beforeTextChanged(
        s: CharSequence?, start: Int,
        count: Int, after: Int
    ) {}

    override fun onTextChanged(
        s: CharSequence?, start: Int,
        before: Int, count: Int
    ) {}

    override fun afterTextChanged(s: Editable?) {
        val len = s?.length ?: 0
        counterText.text = "$len / 200"
    }
})

W przykładzie metoda afterTextChanged otrzymuje bieżącą zawartość pola przez parametr s typu Editable. Długość tekstu aktualizowana jest w osobnym TextView. Aby uniknąć rekurencji w tym przypadku, zmieniany jest tylko counterText, a nie sam EditText, więc pętla nie powstaje. Przy limicie 200 znaków można dodatkowo blokować wprowadzanie po przekroczeniu.

Metody beforeTextChanged i onTextChanged pozostają puste, ponieważ do liczenia długości wystarczy stan końcowy. Jeśli potrzebne będzie logowanie każdej zmiany, kod można dodać w onTextChanged. Taka elastyczność czyni TextWatcher uniwersalnym narzędziem do dowolnych scenariuszy pracy z wprowadzaniem tekstu.

TextWatcher do walidacji pól w czasie rzeczywistym

Walidacja w czasie rzeczywistym znacząco poprawia UX: użytkownik widzi błąd natychmiast po wprowadzeniu niepoprawnej wartości, a nie po naciśnięciu przycisku wysyłania. TextWatcher umożliwia błyskawiczne sprawdzanie emaila, hasła, numeru telefonu i innych pól. Wynik wyświetlany jest przez setError w EditText lub przez osobny TextView z komunikatem o błędzie.

kotlin
fun validateEmail(emailEditText: EditText) {
    emailEditText.addTextChangedListener(object : TextWatcher {
        override fun afterTextChanged(s: Editable?) {
            val email = s?.toString () ?: ""
            if (email.isNotBlank() &&
                !Patterns.EMAIL_ADDRESS.matcher(email).matches()) {
                emailEditText.error = "Invalid email address"
            } else {
                emailEditText.error = null
            }
        }

        override fun beforeTextChanged(...) {}
        override fun onTextChanged(...) {}
    })
}

W przykładzie użyto wbudowanego Patterns.EMAIL_ADDRESS z Android SDK do sprawdzania emaila. Jeśli tekst nie jest pusty i nie pasuje do wzorca, polu ustawiany jest błąd przez właściwość error. Przy poprawnym wprowadzeniu błąd jest usuwany. Ważne, aby nie uruchamiać walidacji na pustym polu — użytkownik może jeszcze nie zacząć wprowadzania, a komunikat o błędzie będzie przedwczesny.

Dla haseł i numerów telefonów stosuje się niestandardowe wyrażenia regularne lub specjalistyczne biblioteki. Na przykład do sprawdzania złożoności hasła można policzyć liczbę cyfr, liter wielkich i małych. TextWatcher umożliwia aktualizację wskaźnika złożoności hasła w czasie rzeczywistym, co pozytywnie wpływa na konwersję rejestracji.

Typowe błędy przy pracy z TextWatcher

Pierwszym i najbardziej krytycznym błędem jest wywołanie rekurencyjne. Jeśli wewnątrz afterTextChanged zmieni się tekst tego samego EditText (przez s.clear(), s.append() lub s.insert()), TextWatcher zadziała ponownie. Tworzy to nieskończoną pętlę, która kończy się StackOverflowError. Rozwiązaniem jest użycie flagi blokującej isUpdating lub sprawdzanie, czy tekst rzeczywiście się zmienił.

Drugim częstym problemem jest wyciek pamięci. TextWatcher zawiera niejawną referencję do Activity lub Fragment przez anonimową klasę. Jeśli nie usunie się listenera przy zniszczeniu View, garbage collector nie będzie mógł zwolnić pamięci. Rozwiązaniem jest użycie komponentów cyklu życia lub jawne wywołanie removeTextChangedListener w onDestroyView.

Trzecim błędem jest użycie niewłaściwej metody. Niektórzy deweloperzy wykonują końcową walidację w onTextChanged, nie czekając na afterTextChanged. W onTextChanged tekst nie jest jeszcze w pełni zaktualizowany i odczyt końcowej wartości może zwrócić nieprawidłowe dane. Właściwym podejściem jest umieszczenie całej logiki odczytu i sprawdzania końcowego tekstu w afterTextChanged.

MetodaMoment wywołaniaPrzeznaczenieCzy można czytać końcowy tekst?
beforeTextChangedPrzed zmianąZapisanie poprzedniego stanuTak
onTextChangedW trakcie zmianyLogowanie, animacjaNie
afterTextChangedPo zmianieWalidacja, liczenie, aktualizacja UITak

Czwartym błędem jest wielokrotne dodawanie TextWatcher. Jeśli addTextChangedListener został wywołany kilka razy dla jednego EditText, wszystkie listenery będą przetwarzać tę samą zmianę. W formularzach z dynamicznym dodawaniem View prowadzi to do duplikowania sprawdzeń i nieprzewidywalnego zachowania. Zawsze sprawdzaj, czy listener nie został już dodany wcześniej, lub używaj pojedynczej instancji.

Często zadawane pytania

Czym różni się onTextChanged od afterTextChanged?

OnTextChanged jest wywoływany w momencie zmiany tekstu, gdy nowe znaki nie zostały jeszcze dodane. Ta metoda nadaje się do animacji i logowania. AfterTextChanged jest wywoływany po pełnym zastosowaniu zmian i daje dostęp do końcowego tekstu przez parametr Editable. Do walidacji i odczytu wartości używaj afterTextChanged.

Jak uniknąć rekurencyjnego wywołania TextWatcher?

Użyj flagi blokującej typu Boolean, która jest ustawiana na true przed zmianą tekstu wewnątrz afterTextChanged. Na początku metody sprawdzaj flagę: jeśli true — wyjdź. Alternatywnie można porównywać starą i nową wartość i zmieniać tekst tylko przy rzeczywistej różnicy.

Czy trzeba usuwać TextWatcher przy zniszczeniu Activity?

Tak, koniecznie. Anonimowa klasa TextWatcher przechowuje referencję do Activity przez domknięcie. Jeśli listener nie zostanie usunięty, Activity nie może być zebrane przez garbage collector. Zawsze wywołuj removeTextChangedListener w onDestroyView dla Fragment lub onDestroy dla Activity.

Czy można używać TextWatcher w RecyclerView?

Tak, ale ostrożnie. W RecyclerView ViewHolder są ponownie używane, a TextWatcher z poprzedniej pozycji może pozostać aktywny. Zawsze usuwaj stary TextWatcher przed ustawieniem nowego w metodzie onBindViewHolder. Używaj tagów lub oddzielnych pól ViewHolder do przechowywania referencji do listenera.

Która metoda jest najlepsza do wyszukiwania z autouzupełnianiem?

Do pola wyszukiwania używaj afterTextChanged w połączeniu z debounce (opóźnieniem). Zaimplementuj timer na 300-500 ms, który resetuje się przy każdej nowej zmianie tekstu. Zapobiega to wysyłaniu zapytania na serwer przy każdym naciśnięciu klawisza i zmniejsza obciążenie API.

Podsumowanie

  • TextWatcher — interfejs Androida do śledzenia zmian tekstu w EditText i TextView, implementujący trzy metody zwrotne.
  • Metoda afterTextChanged — optymalny wybór do walidacji i odczytu końcowego tekstu po zmianach.
  • Wywołanie rekurencyjne — główne zagrożenie TextWatcher, zapobiegane flagą blokującą.
  • Usunięcie listenera jest obowiązkowe, aby zapobiec wyciekowi pamięci przy zniszczeniu Activity lub Fragment.
  • Walidacja w czasie rzeczywistym z TextWatcher poprawia UX i umożliwia błyskawiczne wyświetlanie błędów.
  • Debounce jest niezbędny przy implementacji wyszukiwania i autouzupełniania w celu zmniejszenia obciążenia serwera.
  • Właściwy wybór metody — klucz do stabilnej pracy: beforeTextChanged do zapisywania stanu, onTextChanged do logów, afterTextChanged do końcowego sprawdzenia.

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ż