TextWatcher è un'interfaccia Android che consente di tracciare le modifiche al testo in EditText e altri TextView in tempo reale. Lo sviluppatore riceve notifiche in tre fasi: prima della modifica, durante la modifica e dopo la modifica del contenuto testuale. Secondo Android Developers, 2026, TextWatcher viene utilizzato nella maggior parte delle applicazioni per la validazione dell'input, il conteggio dei caratteri, l'implementazione della ricerca con completamento automatico e la formattazione dinamica del testo. L'interfaccia è indispensabile nei moduli in cui è richiesta una reazione immediata a ogni pressione di tasto.
Punti chiave
TextWatcher è un'interfaccia del pacchetto android.text che notifica all'applicazione le modifiche al testo negli oggetti Editable. A ogni inserimento, eliminazione o sostituzione di carattere, TextWatcher chiama sequenzialmente tre metodi, trasmettendo informazioni sulla posizione delle modifiche. Ciò consente allo sviluppatore di reagire istantaneamente alle azioni dell'utente — senza pulsanti o trigger aggiuntivi.
I principali casi d'uso includono la validazione dei campi in tempo reale: verificare l'email durante la digitazione di ogni carattere, contare i caratteri rimanenti in un campo con limite di lunghezza, implementare la ricerca con richiesta differita tramite debounce. TextWatcher viene utilizzato anche per formattare l'input — ad esempio, l'inserimento automatico di spazi in un numero di telefono o l'aggiunta di una maschera per una data.
Secondo Android Developers, TextWatcher è presente nel 70% delle applicazioni che lavorano con moduli. Librerie come Material Design Components e TextInputEditText usano TextWatcher internamente per gestire gli stati di errore e visualizzare i contatori. Comprendere il funzionamento di questa interfaccia è essenziale per ogni sviluppatore Android.
TextWatcher si collega a qualsiasi oggetto TextView o EditText tramite il metodo addTextChangedListener. Quando l'utente digita o elimina un carattere, Android chiama prima beforeTextChanged, poi onTextChanged e infine afterTextChanged. I parametri di ciascun metodo contengono dati sull'intervallo modificato: posizione iniziale, numero di caratteri eliminati e numero di caratteri aggiunti.
È importante capire che dopo aver chiamato afterTextChanged, l'oggetto Editable contiene già il valore corrente. Pertanto, è conveniente verificare il testo finale del campo in afterTextChanged. Prima di quel momento, i dati non sono ancora completamente aggiornati. Gli sviluppatori spesso confondono lo scopo dei metodi e usano onTextChanged per la validazione finale, mentre la scelta corretta è afterTextChanged.
A ogni inserimento, sostituzione o eliminazione di carattere, la catena di chiamate è garantita per essere eseguita completamente. Tuttavia, se il testo viene modificato all'interno di afterTextChanged (tramite clear, append, insert), TextWatcher si attiverà in modo ricorsivo. Questa è la causa più comune di StackOverflowError nei moduli Android. Per prevenire la ricorsione, si utilizza un flag di blocco.
Ciascuno dei tre metodi svolge il proprio ruolo nel ciclo di vita della modifica del testo. Il metodo beforeTextChanged(CharSequence s, int start, int count, int after) viene chiamato prima dell'applicazione delle modifiche. Trasmette lo stato corrente della stringa, la posizione iniziale della modifica, il numero di caratteri da eliminare e il numero da aggiungere. Qui puoi salvare il valore precedente o verificare le condizioni prima della modifica.
Il metodo onTextChanged viene chiamato durante la modifica, quando i caratteri sono già stati rimossi ma quelli nuovi non sono ancora stati inseriti. Parametri: il testo dopo l'eliminazione, posizione iniziale, numero di caratteri eliminati e numero di caratteri aggiunti. Questo metodo è comodo per animazioni o registrazione, ma non per lavorare con il testo finale effettivo — non è ancora stato assemblato.
Il metodo afterTextChanged è il più richiesto. Riceve un oggetto Editable e viene chiamato dopo che le modifiche sono state completamente applicate. In questo metodo puoi leggere il valore finale del campo, eseguire la validazione, aggiornare l'UI e modificare il testo (con cautela a causa della ricorsione).
Un esempio pratico è un contatore di caratteri per un campo di input che si aggiorna a ogni modifica del testo. Tale elemento si trova spesso nei moduli di feedback, nei post e nei messaggi con limite di lunghezza. L'implementazione tramite TextWatcher richiede poche righe e non necessita di librerie di terze parti.
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"
}
})
Nell'esempio, il metodo afterTextChanged riceve il contenuto corrente del campo tramite il parametro s di tipo Editable. La lunghezza del testo viene aggiornata in un TextView separato. In questo caso, viene modificato solo counterText, non l'EditText stesso, quindi non si verifica alcun ciclo. Per un limite di 200 caratteri, puoi bloccare ulteriormente l'input dopo il superamento.
I metodi beforeTextChanged e onTextChanged rimangono vuoti, poiché lo stato finale è sufficiente per il conteggio della lunghezza. Se è necessario registrare ogni modifica, è possibile aggiungere codice in onTextChanged. Questa flessibilità rende TextWatcher uno strumento universale per qualsiasi scenario di input di testo.
La validazione in tempo reale migliora significativamente l'UX: l'utente vede un errore immediatamente dopo aver inserito un valore errato, anziché dopo aver cliccato il pulsante di invio. TextWatcher consente la verifica istantanea di email, password, numero di telefono e altri campi. Il risultato viene visualizzato tramite setError sull'EditText o tramite un TextView separato con un messaggio di errore.
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(...) {}
})
}
L'esempio utilizza il Patterns.EMAIL_ADDRESS integrato dell'Android SDK per verificare l'email. Se il testo non è vuoto e non corrisponde al modello, viene impostato un errore sul campo tramite la proprietà error. Quando l'input è corretto, l'errore viene cancellato. È importante non eseguire la validazione su un campo vuoto — l'utente potrebbe non aver ancora iniziato a digitare e un messaggio di errore sarebbe prematuro.
Per password e numeri di telefono, si utilizzano espressioni regolari personalizzate o librerie specializzate. Ad esempio, per verificare la complessità della password, puoi contare il numero di cifre, lettere maiuscole e minuscole. TextWatcher consente di aggiornare l'indicatore di forza della password in tempo reale, il che influisce positivamente sulla conversione di registrazione.
Il primo e più critico errore è la chiamata ricorsiva. Se modifichi il testo dello stesso EditText all'interno di afterTextChanged (tramite s.clear(), s.append() o s.insert()), TextWatcher si attiverà di nuovo. Questo crea un ciclo infinito che termina con StackOverflowError. La soluzione è usare un flag di blocco isUpdating o verificare se il testo è effettivamente cambiato.
Il secondo problema comune è la perdita di memoria. TextWatcher mantiene un riferimento implicito all'Activity o al Fragment tramite una classe anonima. Se il listener non viene rimosso alla distruzione della View, il garbage collector non può liberare la memoria. La soluzione è utilizzare componenti del ciclo di vita o chiamare esplicitamente removeTextChangedListener in onDestroyView.
Il terzo errore è usare il metodo sbagliato. Alcuni sviluppatori eseguono la validazione finale in onTextChanged, senza attendere afterTextChanged. In onTextChanged, il testo non è ancora completamente aggiornato e la lettura del valore finale può restituire dati errati. L'approccio corretto è che tutta la logica di lettura e verifica del testo finale dovrebbe essere in afterTextChanged.
| Metodo | Momento della chiamata | Scopo | Può leggere il testo finale? |
|---|---|---|---|
| beforeTextChanged | Prima della modifica | Salvare lo stato precedente | Sì |
| onTextChanged | Durante la modifica | Registrazione, animazione | No |
| afterTextChanged | Dopo la modifica | Validazione, conteggio, aggiornamento UI | Sì |
Il quarto errore è l'aggiunta multipla di TextWatcher. Se addTextChangedListener viene chiamato più volte per lo stesso EditText, tutti i listener elaboreranno la stessa modifica. Nei moduli con aggiunta dinamica di View, ciò porta a controlli duplicati e comportamento imprevedibile. Verifica sempre se il listener è già stato aggiunto o utilizza una singola istanza.
Domande frequenti
OnTextChanged viene chiamato nel momento della modifica del testo, quando i nuovi caratteri non sono ancora stati aggiunti. Questo metodo è adatto per animazioni e registrazione. AfterTextChanged viene chiamato dopo la completa applicazione delle modifiche e fornisce accesso al testo finale tramite il parametro Editable. Per validazione e lettura dei valori, usa afterTextChanged.
Usa un flag di blocco di tipo Boolean, impostato a true prima di modificare il testo all'interno di afterTextChanged. Controlla il flag all'inizio del metodo: se true — esci. In alternativa, confronta i valori vecchio e nuovo e modifica il testo solo quando c'è una differenza effettiva.
Sì, assolutamente. La classe anonima TextWatcher mantiene un riferimento all'Activity tramite una chiusura. Se il listener non viene rimosso, l'Activity non può essere raccolta dal garbage collector. Chiama sempre removeTextChangedListener in onDestroyView per Fragment o onDestroy per Activity.
Sì, ma con attenzione. In RecyclerView, i ViewHolder vengono riutilizzati e un TextWatcher da una posizione precedente può rimanere attivo. Rimuovi sempre il vecchio TextWatcher prima di impostarne uno nuovo nel metodo onBindViewHolder. Usa tag o campi separati del ViewHolder per memorizzare il riferimento al listener.
Per un campo di ricerca, usa afterTextChanged in combinazione con debounce (ritardo). Implementa un timer di 300-500 ms che si azzera a ogni nuova modifica del testo. Ciò impedisce l'invio di una richiesta al server a ogni pressione di tasto e riduce il carico dell'API.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche