TextWatcher is een Android-interface waarmee u tekstwijzigingen in EditText en andere TextView in realtime kunt volgen. De ontwikkelaar ontvangt meldingen in drie fasen: vóór de wijziging, tijdens de wijziging en na de wijziging van de tekstinhoud. Volgens Android Developers, 2026 wordt TextWatcher gebruikt in de meeste toepassingen voor invoervalidatie, teken tellen, implementatie van zoeken met automatisch aanvullen en dynamische tekstopmaak. De interface is onmisbaar in formulieren waar onmiddellijke reactie op elke toetsaanslag vereist is.
Belangrijkste punten
TextWatcher is een interface uit het android.text-pakket dat de applicatie op de hoogte stelt van tekstwijzigingen in Editable-objecten. Bij elke invoer, verwijdering of vervanging van een teken roept TextWatcher achtereenvolgens drie methoden aan, waarbij informatie over de positie van de wijzigingen wordt doorgegeven. Hiermee kan de ontwikkelaar onmiddellijk reageren op gebruikersacties — zonder extra knoppen of triggers.
De belangrijkste gebruiksscenario's omvatten realtime veldvalidatie: controle van e-mail bij elk ingevoerd teken, tellen van resterende tekens in een veld met lengtebeperking, implementatie van zoeken met uitgestelde verzoekverzending via debounce. TextWatcher wordt ook gebruikt voor invoeropmaak — bijvoorbeeld automatisch plaatsen van spaties in een telefoonnummer of toevoegen van een masker voor een datum.
Volgens Android Developers is TextWatcher aanwezig in 70% van de applicaties die met formulieren werken. Bibliotheken zoals Material Design Components en TextInputEditText gebruiken TextWatcher intern voor foutstatusbeheer en weergave van tellers. Begrip van hoe deze interface werkt is essentieel voor elke Android-ontwikkelaar.
TextWatcher wordt via de methode addTextChangedListener aan elk TextView- of EditText-object gekoppeld. Wanneer de gebruiker een teken invoert of verwijdert, roept Android eerst beforeTextChanged aan, vervolgens onTextChanged en tenslotte afterTextChanged. In de parameters van elke methode worden gegevens over het gewijzigde bereik doorgegeven: startpositie, aantal verwijderde tekens en aantal toegevoegde tekens.
Het is belangrijk te begrijpen dat na het aanroepen van afterTextChanged het Editable-object al de actuele waarde bevat. Daarom is het in afterTextChanged handig om de uiteindelijke tekst van het veld te controleren. Tot dat moment zijn de gegevens nog niet volledig bijgewerkt. Ontwikkelaars verwarren vaak het doel van de methoden en gebruiken onTextChanged voor eindvalidatie, terwijl de juiste keuze afterTextChanged is.
Bij elke invoeging, vervanging of verwijdering van een teken wordt de aanroepketen gegarandeerd volledig uitgevoerd. Als de tekst echter binnen afterTextChanged wordt gewijzigd (via clear, append, insert), wordt TextWatcher recursief geactiveerd. Dit is de meest voorkomende oorzaak van StackOverflowError in Android-formulieren. Om recursie te voorkomen wordt een blokkeervlag gebruikt.
Elk van de drie methoden speelt zijn rol in de levenscyclus van tekstwijziging. De methode beforeTextChanged(CharSequence s, int start, int count, int after) wordt aangeroepen voordat wijzigingen worden toegepast. Het geeft de huidige toestand van de string door, de startpositie van de wijziging, het aantal te verwijderen tekens en het aantal toe te voegen tekens. Hier kan de vorige waarde worden opgeslagen of kunnen voorwaarden vóór wijziging worden gecontroleerd.
De methode onTextChanged wordt aangeroepen tijdens de wijziging, wanneer tekens al zijn verwijderd maar nieuwe nog niet zijn ingevoegd. Parameters: tekst na verwijdering, startpositie, aantal verwijderde tekens en aantal toe te voegen tekens. Deze methode is handig voor animatie of logging, maar niet voor het werken met de actuele uiteindelijke tekst — deze is nog niet samengesteld.
De methode afterTextChanged is de meest gevraagde. Deze ontvangt een Editable-object en wordt aangeroepen nadat wijzigingen volledig zijn toegepast. In deze methode kan de uiteindelijke waarde van het veld worden gelezen, validatie worden uitgevoerd, de UI worden bijgewerkt en de tekst worden gewijzigd (met voorzichtigheid vanwege recursie).
Praktisch voorbeeld — een teken teller voor een invoerveld dat bij elke tekstwijziging wordt bijgewerkt. Zo'n element komt vaak voor in contactformulieren, berichten en posts met lengtebeperking. Implementatie via TextWatcher kost slechts een paar regels en vereist geen externe bibliotheken.
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"
}
})
In het voorbeeld ontvangt de methode afterTextChanged de huidige inhoud van het veld via parameter s van het type Editable. De tekstlengte wordt bijgewerkt in een aparte TextView. Om recursie in dit geval te voorkomen, wordt alleen counterText gewijzigd, niet de EditText zelf, dus er ontstaat geen lus. Bij een limiet van 200 tekens kan invoer na overschrijding aanvullend worden geblokkeerd.
De methoden beforeTextChanged en onTextChanged blijven leeg, omdat voor het tellen van de lengte de eindtoestand voldoende is. Als elke wijziging moet worden gelogd, kan code aan onTextChanged worden toegevoegd. Deze flexibiliteit maakt TextWatcher tot een universeel hulpmiddel voor elk scenario van tekstinvoer.
Realtime validatie verbetert de UX aanzienlijk: de gebruiker ziet de fout onmiddellijk na het invoeren van een onjuiste waarde, niet pas na het indrukken van de verzendknop. TextWatcher maakt directe controle van e-mail, wachtwoord, telefoonnummer en andere velden mogelijk. Het resultaat wordt weergegeven via setError op EditText of via een aparte TextView met een foutmelding.
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(...) {}
})
}
In het voorbeeld wordt de ingebouwde Patterns.EMAIL_ADDRESS uit de Android SDK gebruikt voor e-mailcontrole. Als de tekst niet leeg is en niet overeenkomt met het patroon, wordt via de eigenschap error een fout aan het veld toegewezen. Bij correcte invoer wordt de fout gewist. Het is belangrijk om validatie niet op een leeg veld uit te voeren — de gebruiker is mogelijk nog niet begonnen met invoeren en de foutmelding zou voorbarig zijn.
Voor wachtwoorden en telefoonnummers worden aangepaste reguliere expressies of gespecialiseerde bibliotheken gebruikt. Bijvoorbeeld, voor wachtwoordcomplexiteit kan het aantal cijfers, hoofdletters en kleine letters worden geteld. TextWatcher maakt het mogelijk de wachtwoordsterkte-indicator in realtime bij te werken, wat een positieve invloed heeft op de registratieconversie.
De eerste en meest kritieke fout — recursieve aanroep. Als binnen afterTextChanged de tekst van dezelfde EditText wordt gewijzigd (via s.clear(), s.append() of s.insert()), wordt TextWatcher opnieuw geactiveerd. Dit creëert een oneindige lus die eindigt met StackOverflowError. De oplossing — gebruik een blokkeervlag isUpdating of controleer of de tekst daadwerkelijk is veranderd.
Het tweede veelvoorkomende probleem — geheugenlek. TextWatcher bevat via een anonieme klasse een impliciete verwijzing naar Activity of Fragment. Als de listener niet wordt verwijderd bij vernietiging van de View, kan de garbage collector het geheugen niet vrijgeven. De oplossing — gebruik levenscycluscomponenten of roep expliciet removeTextChangedListener aan in onDestroyView.
De derde fout — gebruik van de verkeerde methode. Sommige ontwikkelaars voeren eindvalidatie uit in onTextChanged, zonder te wachten op afterTextChanged. In onTextChanged is de tekst nog niet volledig bijgewerkt en het lezen van de uiteindelijke waarde kan onjuiste gegevens opleveren. De juiste benadering — plaats alle logica voor het lezen en controleren van de uiteindelijke tekst in afterTextChanged.
| Methode | Aanroepmoment | Doel | Kan eindtekst worden gelezen? |
|---|---|---|---|
| beforeTextChanged | Vóór wijziging | Opslaan vorige toestand | Ja |
| onTextChanged | Tijdens wijziging | Logging, animatie | Nee |
| afterTextChanged | Na wijziging | Validatie, tellen, UI bijwerken | Ja |
De vierde fout — meerdere keren toevoegen van TextWatcher. Als addTextChangedListener meerdere keren voor dezelfde EditText is aangeroepen, zullen alle listeners dezelfde wijziging verwerken. In formulieren met dynamische toevoeging van Views leidt dit tot duplicatie van controles en onvoorspelbaar gedrag. Controleer altijd of de listener al eerder is toegevoegd of gebruik een enkele instantie.
Veelgestelde vragen
OnTextChanged wordt aangeroepen op het moment van tekstwijziging, wanneer nieuwe tekens nog niet zijn toegevoegd. Deze methode is geschikt voor animatie en logging. AfterTextChanged wordt aangeroepen na volledige toepassing van de wijzigingen en geeft toegang tot de uiteindelijke tekst via de parameter Editable. Gebruik afterTextChanged voor validatie en het lezen van de waarde.
Gebruik een blokkeervlag van het type Boolean die op true wordt gezet voordat de tekst binnen afterTextChanged wordt gewijzigd. Controleer aan het begin van de methode de vlag: als deze true is — verlaat de methode. Als alternatief kunt u de oude en nieuwe waarde vergelijken en de tekst alleen wijzigen bij een daadwerkelijk verschil.
Ja, absoluut. De anonieme klasse van TextWatcher houdt via een closure een verwijzing naar Activity vast. Als de listener niet wordt verwijderd, kan de Activity niet door de garbage collector worden opgehaald. Roep altijd removeTextChangedListener aan in onDestroyView voor Fragment of onDestroy voor Activity.
Ja, maar met voorzichtigheid. In RecyclerView worden ViewHolders hergebruikt en de TextWatcher van de vorige positie kan actief blijven. Verwijder altijd de oude TextWatcher voordat u een nieuwe instelt in de methode onBindViewHolder. Gebruik tags of aparte velden van ViewHolder om de verwijzing naar de listener op te slaan.
Gebruik voor het zoekveld afterTextChanged in combinatie met debounce (vertraging). Implementeer een timer van 300-500 ms die wordt gereset bij elke nieuwe tekstwijziging. Dit voorkomt dat bij elke toetsaanslag een verzoek naar de server wordt gestuurd en vermindert de belasting van de API.
Samenvatting
We ontwikkelen een mobiele applicatie turnkey
IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.
Lees ook