TextWatcher é uma interface Android que permite rastrear alterações de texto em EditText e outros TextView em tempo real. O desenvolvedor recebe notificações em três estágios: antes da alteração, durante a alteração e após a alteração do conteúdo do texto. De acordo com Android Developers, 2026, o TextWatcher é usado na maioria dos aplicativos para validação de entrada, contagem de caracteres, implementação de pesquisa com autocompletar e formatação dinâmica de texto. A interface é indispensável em formulários onde é necessária uma reação imediata a cada pressionamento de tecla.
Principais pontos
TextWatcher é uma interface do pacote android.text que notifica o aplicativo sobre alterações de texto em objetos Editable. A cada entrada, exclusão ou substituição de caractere, o TextWatcher chama sequencialmente três métodos, transmitindo informações sobre a posição das alterações. Isso permite que o desenvolvedor reaja às ações do usuário instantaneamente — sem botões ou gatilhos adicionais.
Os principais casos de uso incluem validação de campos em tempo real: verificar e-mail enquanto cada caractere é digitado, contar caracteres restantes em um campo com limite de comprimento, implementar pesquisa com solicitação adiada via debounce. TextWatcher também é usado para formatar entrada — por exemplo, inserção automática de espaços em um número de telefone ou adição de uma máscara para data.
De acordo com Android Developers, o TextWatcher está presente em 70% dos aplicativos que trabalham com formulários. Bibliotecas como Material Design Components e TextInputEditText usam TextWatcher internamente para gerenciar estados de erro e exibir contadores. Entender como essa interface funciona é essencial para todo desenvolvedor Android.
TextWatcher conecta-se a qualquer objeto TextView ou EditText através do método addTextChangedListener. Quando o usuário digita ou exclui um caractere, o Android primeiro chama beforeTextChanged, depois onTextChanged e finalmente afterTextChanged. Os parâmetros de cada método contêm dados sobre o intervalo alterado: posição inicial, número de caracteres excluídos e número de caracteres adicionados.
É importante entender que após chamar afterTextChanged, o objeto Editable já contém o valor atual. Portanto, é conveniente verificar o texto final do campo em afterTextChanged. Antes desse ponto, os dados ainda não estão totalmente atualizados. Os desenvolvedores frequentemente confundem o propósito dos métodos e usam onTextChanged para validação final, embora a escolha correta seja afterTextChanged.
A cada inserção, substituição ou exclusão de caractere, a cadeia de chamadas é garantidamente executada por completo. No entanto, se o texto for alterado dentro de afterTextChanged (via clear, append, insert), o TextWatcher será acionado recursivamente. Esta é a causa mais comum de StackOverflowError em formulários Android. Para evitar a recursão, usa-se um sinalizador de bloqueio.
Cada um dos três métodos desempenha seu papel no ciclo de vida da alteração de texto. O método beforeTextChanged(CharSequence s, int start, int count, int after) é chamado antes da aplicação das alterações. Ele transmite o estado atual da string, a posição inicial da alteração, o número de caracteres sendo excluídos e o número sendo adicionados. Aqui você pode salvar o valor anterior ou verificar condições antes da modificação.
O método onTextChanged é chamado durante a alteração, quando os caracteres já foram removidos, mas os novos ainda não foram inseridos. Parâmetros: o texto após a exclusão, posição inicial, número de caracteres excluídos e número de caracteres adicionados. Este método é conveniente para animação ou registro, mas não para trabalhar com o texto final real — ele ainda não foi montado.
O método afterTextChanged é o mais requisitado. Ele recebe um objeto Editable e é chamado após as alterações terem sido totalmente aplicadas. Neste método, você pode ler o valor final do campo, realizar validação, atualizar a UI e modificar o texto (com cuidado devido à recursão).
Um exemplo prático é um contador de caracteres para um campo de entrada que é atualizado a cada alteração de texto. Esse elemento é frequentemente encontrado em formulários de feedback, postagens e mensagens com limite de comprimento. A implementação via TextWatcher requer algumas linhas e não precisa de bibliotecas de terceiros.
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"
}
})
No exemplo, o método afterTextChanged recebe o conteúdo atual do campo através do parâmetro s do tipo Editable. O comprimento do texto é atualizado em um TextView separado. Neste caso, apenas counterText é modificado, não o EditText em si, portanto nenhum loop ocorre. Para um limite de 200 caracteres, você pode bloquear adicionalmente a entrada após excedê-lo.
Os métodos beforeTextChanged e onTextChanged permanecem vazios, pois o estado final é suficiente para a contagem de comprimento. Se você precisar registrar cada alteração, o código pode ser adicionado em onTextChanged. Essa flexibilidade torna o TextWatcher uma ferramenta universal para qualquer cenário de entrada de texto.
A validação em tempo real melhora significativamente a UX: o usuário vê um erro imediatamente após inserir um valor incorreto, em vez de após clicar no botão de envio. TextWatcher permite a verificação instantânea de e-mail, senha, número de telefone e outros campos. O resultado é exibido através de setError no EditText ou através de um TextView separado com uma mensagem de erro.
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(...) {}
})
}
O exemplo usa o Patterns.EMAIL_ADDRESS integrado do Android SDK para verificar o e-mail. Se o texto não estiver vazio e não corresponder ao padrão, um erro é definido no campo através da propriedade error. Quando a entrada está correta, o erro é limpo. É importante não executar a validação em um campo vazio — o usuário pode ainda não ter começado a digitar, e uma mensagem de erro seria prematura.
Para senhas e números de telefone, são usadas expressões regulares personalizadas ou bibliotecas especializadas. Por exemplo, para verificar a complexidade da senha, você pode contar o número de dígitos, letras maiúsculas e minúsculas. TextWatcher permite atualizar o indicador de força da senha em tempo real, o que afeta positivamente a conversão de registro.
O primeiro e mais crítico erro é a chamada recursiva. Se você alterar o texto do mesmo EditText dentro de afterTextChanged (via s.clear(), s.append() ou s.insert()), o TextWatcher será acionado novamente. Isso cria um loop infinito que termina em StackOverflowError. A solução é usar um sinalizador de bloqueio isUpdating ou verificar se o texto realmente mudou.
O segundo problema comum é o vazamento de memória. TextWatcher mantém uma referência implícita à Activity ou Fragment através de uma classe anônima. Se o listener não for removido quando a View for destruída, o coletor de lixo não pode liberar a memória. A solução é usar componentes de ciclo de vida ou chamar explicitamente removeTextChangedListener em onDestroyView.
O terceiro erro é usar o método errado. Alguns desenvolvedores realizam a validação final em onTextChanged, sem esperar por afterTextChanged. Em onTextChanged, o texto ainda não está totalmente atualizado, e a leitura do valor final pode retornar dados incorretos. A abordagem correta é que toda a lógica de leitura e verificação do texto final deve estar em afterTextChanged.
| Método | Momento da chamada | Propósito | Pode ler o texto final? |
|---|---|---|---|
| beforeTextChanged | Antes da alteração | Salvar estado anterior | Sim |
| onTextChanged | Durante a alteração | Registro, animação | Não |
| afterTextChanged | Após a alteração | Validação, contagem, atualização de UI | Sim |
O quarto erro é a adição múltipla de TextWatcher. Se addTextChangedListener for chamado várias vezes para o mesmo EditText, todos os listeners processarão a mesma alteração. Em formulários com adição dinâmica de Views, isso leva a verificações duplicadas e comportamento imprevisível. Sempre verifique se o listener já foi adicionado ou use uma única instância.
Perguntas frequentes
OnTextChanged é chamado no momento da alteração do texto, quando novos caracteres ainda não foram adicionados. Este método é adequado para animação e registro. AfterTextChanged é chamado após a aplicação completa das alterações e fornece acesso ao texto final através do parâmetro Editable. Para validação e leitura de valores, use afterTextChanged.
Use um sinalizador de bloqueio do tipo Boolean, que é definido como true antes de alterar o texto dentro de afterTextChanged. Verifique o sinalizador no início do método: se true — saia. Alternativamente, compare os valores antigo e novo e altere o texto apenas quando houver uma diferença real.
Sim, absolutamente. A classe anônima TextWatcher mantém uma referência à Activity através de um closure. Se o listener não for removido, a Activity não pode ser coletada pelo coletor de lixo. Sempre chame removeTextChangedListener em onDestroyView para Fragment ou onDestroy para Activity.
Sim, mas com cuidado. No RecyclerView, os ViewHolders são reutilizados, e um TextWatcher de uma posição anterior pode permanecer ativo. Sempre remova o TextWatcher antigo antes de definir um novo no método onBindViewHolder. Use tags ou campos separados do ViewHolder para armazenar a referência ao listener.
Para um campo de pesquisa, use afterTextChanged em combinação com debounce (atraso). Implemente um temporizador de 300-500 ms que é reiniciado a cada nova alteração de texto. Isso evita enviar uma solicitação ao servidor a cada pressionamento de tecla e reduz a carga da API.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também