TextWatcher: що це, інтерфейс TextWatcher і реалізація в Android

Автор: IT Sectr Опубліковано: 2026-07-08 Час читання: 6 хв

TextWatcher — це інтерфейс Android, який дозволяє відстежувати зміни тексту в EditText та інших TextView у реальному часі. Розробник отримує сповіщення на трьох етапах: перед зміною, під час зміни та після зміни текстового вмісту. За даними Android Developers, 2026, TextWatcher застосовується в більшості додатків для валідації введення, підрахунку символів, реалізації пошуку з автодоповненням та динамічного форматування тексту. Інтерфейс незамінний у формах, де потрібна негайна реакція на кожне натискання клавіші.

Головне

  • TextWatcher — вбудований інтерфейс Android SDK для прослуховування змін тексту в TextView і EditText.
  • Інтерфейс містить три методи: beforeTextChanged, onTextChanged і afterTextChanged, кожен відповідає за свій етап зміни.
  • Метод afterTextChanged найбільш зручний для валідації поля після завершення введення користувачем.
  • Рекурсивний виклик — часта помилка: зміна тексту всередині TextWatcher призводить до нескінченного циклу.
  • TextWatcher застосовується в полях пошуку, валідації форм, підрахунку символів та автоформатуванні номера телефону.

Що таке TextWatcher і навіщо він потрібен?

TextWatcher — це інтерфейс із пакета android.text, який сповіщає додаток про зміни тексту в об'єктах Editable. При кожному введенні, видаленні або заміні символу TextWatcher послідовно викликає три методи, передаючи інформацію про положення змін. Це дозволяє розробнику реагувати на дії користувача миттєво — без додаткових кнопок або тригерів.

Основні сценарії використання включають валідацію полів у реальному часі: перевірка email при введенні кожного символу, підрахунок символів, що залишилися в полі з обмеженням довжини, реалізація пошуку з відкладеним відправленням запиту через debounce. Також TextWatcher застосовується для форматування введення — наприклад, автоматичне розставлення пробілів у номері телефону або додавання маски для дати.

За даними Android Developers, TextWatcher присутній у 70% додатків, що працюють з формами. Бібліотеки на кшталт Material Design Components і TextInputEditText використовують TextWatcher внутрішньо для керування станом помилки та відображення лічильників. Розуміння роботи цього інтерфейсу необхідне кожному Android-розробнику.

Як працює інтерфейс TextWatcher

TextWatcher під'єднується до будь-якого об'єкта TextView або EditText через метод addTextChangedListener. Коли користувач вводить або видаляє символ, Android викликає спочатку beforeTextChanged, потім onTextChanged і нарешті afterTextChanged. У параметрах кожного методу передаються дані про змінюваний діапазон: стартова позиція, кількість видалених символів та кількість доданих символів.

Важливо розуміти, що після виклику afterTextChanged об'єкт Editable вже містить актуальне значення. Тому саме в afterTextChanged зручно перевіряти підсумковий текст поля. До цього моменту дані ще не повністю оновлені. Розробники часто плутають призначення методів і використовують onTextChanged для фінальної валідації, хоча правильний вибір — afterTextChanged.

Особливості виклику методів

При кожному вставленні, заміні або видаленні символу ланцюжок викликів гарантовано виконується повністю. Однак якщо всередині afterTextChanged змінити текст (через clear, append, insert), TextWatcher спрацює рекурсивно. Це найчастіша причина StackOverflowError у формах Android. Для запобігання рекурсії використовують прапорець-блокування.

Три методи TextWatcher: beforeTextChanged, onTextChanged, afterTextChanged

Кожен із трьох методів виконує свою роль у життєвому циклі зміни тексту. Метод beforeTextChanged(CharSequence s, int start, int count, int after) викликається перед застосуванням змін. Він передає поточний стан рядка, позицію початку зміни, кількість видалених символів і кількість доданих. Тут можна зберегти попереднє значення або перевірити умови до модифікації.

Метод onTextChanged викликається в процесі зміни, коли символи вже видалені, але нові ще не вставлені. Параметри: текст після видалення, стартова позиція, кількість видалених символів і кількість доданих. Цей метод зручний для анімації або логування, але не для роботи з актуальним фінальним текстом — він ще не зібраний.

Метод afterTextChanged — найбільш затребуваний. Він отримує об'єкт Editable і викликається після того, як зміни повністю застосовані. У цьому методі можна читати підсумкове значення поля, виконувати валідацію, оновлювати UI та змінювати текст (з обережністю через рекурсію).

Приклад реалізації TextWatcher для підрахунку символів

Практичний приклад — лічильник символів для поля введення, який оновлюється при кожній зміні тексту. Такий елемент часто зустрічається в формах зворотного зв'язку, постах і повідомленнях з обмеженням довжини. Реалізація через TextWatcher займає кілька рядків і не потребує сторонніх бібліотек.

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"
    }
})

У прикладі метод afterTextChanged отримує поточний вміст поля через параметр s типу Editable. Довжина тексту оновлюється в окремому TextView. Для уникнення рекурсії в даному випадку змінюється лише counterText, а не сам EditText, тому цикл не виникає. При ліміті в 200 символів можна додатково блокувати введення після перевищення.

Методи beforeTextChanged і onTextChanged залишаються порожніми, оскільки для підрахунку довжини достатньо фінального стану. Якщо потрібно логувати кожну зміну, код можна додати в onTextChanged. Така гнучкість робить TextWatcher універсальним інструментом для будь-яких сценаріїв роботи з текстовим введенням.

TextWatcher для валідації полів у реальному часі

Валідація в реальному часі суттєво покращує UX: користувач бачить помилку відразу після введення некоректного значення, а не після натискання кнопки відправлення. TextWatcher дозволяє реалізувати миттєву перевірку email, пароля, номера телефону та інших полів. Результат відображається через setError у EditText або через окремий TextView з повідомленням про помилку.

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(...) {}
    })
}

У прикладі використовується вбудований Patterns.EMAIL_ADDRESS з Android SDK для перевірки email. Якщо текст не порожній і не відповідає шаблону, полю встановлюється помилка через властивість error. При коректному введенні помилка очищається. Важно не запускати валідацію на порожньому полі — користувач може ще не почати введення, і повідомлення про помилку буде передчасним.

Для паролів і номерів телефонів використовують кастомні регулярні вирази або спеціалізовані бібліотеки. Наприклад, для перевірки складності пароля можна підрахувати кількість цифр, літер у верхньому та нижньому регістрі. TextWatcher дозволяє оновлювати індикатор складності пароля в реальному часі, що позитивно впливає на конверсію реєстрації.

Типові помилки при роботі з TextWatcher

Перша і найкритичніша помилка — рекурсивний виклик. Якщо всередині afterTextChanged змінити текст того ж EditText (через s.clear(), s.append() або s.insert()), TextWatcher спрацює повторно. Це створює нескінченний цикл, який закінчується StackOverflowError. Рішення — використовувати прапорець-блокування isUpdating або перевіряти, чи змінився текст насправді.

Друга поширена проблема — витік пам'яті. TextWatcher містить неявне посилання на Activity або Fragment через анонімний клас. Якщо не видаляти listener при знищенні View, збирач сміття не зможе звільнити пам'ять. Рішення — використовувати lifecycle-компоненти або явно викликати removeTextChangedListener в onDestroyView.

Третя помилка — використання неправильного методу. Деякі розробники виконують фінальну валідацію в onTextChanged, не дочекавшись afterTextChanged. У onTextChanged текст ще не повністю оновлений, і читання підсумкового значення може повернути некоректні дані. Правильний підхід — вся логіка читання та перевірки фінального тексту повинна бути в afterTextChanged.

МетодМомент викликуПризначенняЧи можна читати підсумковий текст?
beforeTextChangedДо зміниЗбереження попереднього стануТак
onTextChangedПід час зміниЛогування, анімаціяНі
afterTextChangedПісля зміниВалідація, підрахунок, оновлення UIТак

Четверта помилка — множинне додавання TextWatcher. Якщо addTextChangedListener викликано кілька разів для одного EditText, усі listener будуть обробляти ту саму зміну. У формах з динамічним додаванням View це призводить до дублювання перевірок і непередбачуваної поведінки. Завжди перевіряйте, чи listener вже додано, або використовуйте єдиний екземпляр.

Часто задавані питання

Чим відрізняється onTextChanged від afterTextChanged?

OnTextChanged викликається в момент зміни тексту, коли нові символи ще не додані. Цей метод підходить для анімації та логування. AfterTextChanged викликається після повного застосування змін і дає доступ до підсумкового тексту через параметр Editable. Для валідації та читання значення використовуйте afterTextChanged.

Як уникнути рекурсивного виклику TextWatcher?

Використовуйте прапорець-блокування типу Boolean, який встановлюється в true перед зміною тексту всередині afterTextChanged. На початку методу перевіряйте прапорець: якщо true — виходьте. Альтернативно можна порівнювати старе та нове значення і змінювати текст лише при реальному розходженні.

Чи потрібно видаляти TextWatcher при знищенні Activity?

Так, обов'язково. Анонімний клас TextWatcher утримує посилання на Activity через замикання. Якщо listener не видалено, Activity не може бути зібрана збирачем сміття. Завжди викликайте removeTextChangedListener в onDestroyView для Fragment або onDestroy для Activity.

Чи можна використовувати TextWatcher у RecyclerView?

Так, але з обережністю. У RecyclerView ViewHolder перевикористовуються, і TextWatcher із попередньої позиції може залишитися активним. Завжди видаляйте старий TextWatcher перед встановленням нового в методі onBindViewHolder. Використовуйте теги або окремі поля ViewHolder для зберігання посилання на listener.

Який метод краще для пошуку з автодоповненням?

Для пошукового поля використовуйте afterTextChanged у поєднанні з debounce (затримкою). Реалізуйте таймер на 300-500 мс, який скидається при кожній новій зміні тексту. Це запобігає відправленню запиту на сервер при кожному натисканні клавіші та знижує навантаження на API.

Підсумки

  • TextWatcher — інтерфейс Android для відстеження змін тексту в EditText і TextView, що реалізує три методи зворотного виклику.
  • Метод afterTextChanged — оптимальний вибір для валідації та читання фінального тексту після змін.
  • Рекурсивний виклик — головна небезпека TextWatcher, запобігається прапорцем-блокуванням.
  • Видалення listener обов'язкове для запобігання витоку пам'яті при знищенні Activity або Fragment.
  • Валідація в реальному часі з TextWatcher покращує UX і дозволяє показувати помилки миттєво.
  • Debounce необхідний при реалізації пошуку та автодоповнення для зниження навантаження на сервер.
  • Правильний вибір методу — ключ до стабільної роботи: beforeTextChanged для збереження стану, onTextChanged для логів, afterTextChanged для фінальної перевірки.

Ми розробимо мобільний застосунок під ключ

IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.

Обговорити проект

Читайте також