TextWatcher је Android интерфејс који омогућава праћење промена текста у EditText и другим TextView у реалном времену. Програмер добија обавештења у три фазе: пре промене, током промене и после промене текстуалног садржаја. Према Android Developers, 2026, TextWatcher се примењује у већини апликација за валидацију уноса, бројање знакова, имплементацију претраге са аутоматским допуњавањем и динамичко форматирање текста. Интерфејс је незаменљив у формама где је потребна тренутна реакција на сваки притисак тастера.
Главно
TextWatcher — интерфејс из пакета android.text који обавештава апликацију о променама текста у Editable објектима. При сваком уносу, брисању или замени знака, TextWatcher секвенцијално позива три методе, преносећи информације о положају промена. Ово омогућава програмеру да реагује на радње корисника тренутно — без додатних дугмади или окидача.
Главни сценарији употребе укључују валидацију поља у реалном времену: провера е-поште при уносу сваког знака, бројање преосталих знакова у пољу са ограничењима дужине, имплементација претраге са одложеним слањем захтева путем debounce-а. Такође, TextWatcher се користи за форматирање уноса — на пример, аутоматско постављање размака у броју телефона или додавање маске за датум.
Према Android Developers, TextWatcher је присутан у 70% апликација које раде са формама. Библиотеке попут Material Design Components и TextInputEditText користе TextWatcher интерно за управљање стањем грешке и приказ бројача. Разумевање рада овог интерфејса је неопходно за сваког Android програмера.
TextWatcher се повезује на било који TextView или EditText објекат преко методе addTextChangedListener. Када корисник уноси или брише знак, Android прво позива beforeTextChanged, затим onTextChanged и на крају afterTextChanged. У параметрима сваке методе преносе се подаци о измењеном опсегу: почетна позиција, број избрисаних знакова и број додатих знакова.
Важно је разумети да након позива afterTextChanged објекат Editable већ садржи актуелну вредност. Зато је у afterTextChanged згодно проверавати коначни текст поља. До тог тренутка подаци још нису у потпуности ажурирани. Програмери често мешају намену метода и користе onTextChanged за коначну валидацију, иако је прави избор afterTextChanged.
При сваком уметању, замени или брисању знака ланац позива се гарантовано извршава у потпуности. Међутим, ако се унутар afterTextChanged промени текст (путем clear, append, insert), TextWatcher ће се покренути рекурзивно. Ово је најчешћи узрок StackOverflowError-а у Android формама. За спречавање рекурзије користи се закључавајући флаг.
Свака од три методе има своју улогу у животном циклусу промене текста. Метода beforeTextChanged(CharSequence s, int start, int count, int after) се позива пре примене промена. Она преноси тренутно стање ниске, почетну позицију промене, број знакова који се бришу и број знакова који се додају. Овде се може сачувати претходна вредност или проверити услови пре модификације.
Метода onTextChanged се позива током промене, када су знакови већ избрисани, али нови још нису уметнути. Параметри: текст након брисања, почетна позиција, број избрисаних знакова и број знакова који се додају. Ова метода је погодна за анимацију или логирање, али не за рад са актуелним коначним текстом — он још није састављен.
Метода afterTextChanged је најтраженија. Она прима Editable објекат и позива се након што су промене у потпуности примењене. У овој методи се може читати коначна вредност поља, вршити валидација, ажурирати UI и мењати текст (уз опрез због рекурзије).
Практичан пример — бројач знакова за поље уноса који се ажурира при свакој промени текста. Такав елемент се често среће у формама за контакт, објавама и порукама са ограничењем дужине. Имплементација путем TextWatcher-а захтева неколико линија и не захтева библиотеке трећих страна.
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 универзалним алатом за све сценарије рада са текстуалним уносом.
Валидација у реалном времену значајно побољшава UX: корисник види грешку одмах након уноса неисправне вредности, а не након притиска на дугме за слање. TextWatcher омогућава тренутну проверу е-поште, лозинке, броја телефона и других поља. Резултат се приказује путем setError на EditText-у или кроз засебан TextView са поруком о грешци.
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-а за проверу е-поште. Ако текст није празан и не одговара шаблону, пољу се поставља грешка преко својства error. При исправном уносу грешка се брише. Важно је не покретати валидацију на празном пољу — корисник можда још није почео унос, а порука о грешци би била преурањена.
За лозинке и бројеве телефона користе се прилагођени регуларни изрази или специјализоване библиотеке. На пример, за проверу сложености лозинке може се избројати број цифара, великих и малих слова. TextWatcher омогућава ажурирање индикатора сложености лозинке у реалном времену, што позитивно утиче на конверзију регистрације.
Прва и најкритичнија грешка — рекурзивни позив. Ако се унутар afterTextChanged промени текст истог EditText-а (путем s.clear(), s.append() или s.insert()), TextWatcher ће се поново покренути. Ово ствара бесконачну петљу која се завршава StackOverflowError-ом. Решење — коришћење закључавајућег флага isUpdating или провера да ли се текст стварно променио.
Други чест проблем — цурење меморије. TextWatcher садржи имплицитну референцу на Activity или Fragment преко анонимне класе. Ако се listener не уклони при уништењу View-а, сакупљач отпада не може да ослободи меморију. Решење — коришћење компоненти животног циклуса или експлицитно позивање removeTextChangedListener у onDestroyView.
Трећа грешка — коришћење погрешне методе. Неки програмери врше коначну валидацију у onTextChanged, не чекајући afterTextChanged. У onTextChanged текст још није у потпуности ажуриран и читање коначне вредности може вратити неисправне податке. Правилан приступ — постављање целокупне логике читања и провере коначног текста у afterTextChanged.
| Метода | Тренутак позива | Намена | Може се читати коначни текст? |
|---|---|---|---|
| beforeTextChanged | Пре промене | Чување претходног стања | Да |
| onTextChanged | Током промене | Логирање, анимација | Не |
| afterTextChanged | После промене | Валидација, бројање, ажурирање UI | Да |
Четврта грешка — вишеструко додавање TextWatcher-а. Ако је addTextChangedListener позван више пута за један EditText, сви listener-и ће обрађивати исту промену. У формама са динамичким додавањем View-а, ово доводи до дуплирања провера и непредвидивог понашања. Увек проверавајте да ли је listener већ додат или користите јединствену инстанцу.
Често постављана питања
OnTextChanged се позива у тренутку промене текста, када нови знакови још нису додати. Ова метода је погодна за анимацију и логирање. AfterTextChanged се позива након потпуне примене промена и даје приступ коначном тексту кроз Editable параметар. За валидацију и читање вредности користите afterTextChanged.
Користите закључавајући флаг типа Boolean који се поставља на true пре промене текста унутар afterTextChanged. На почетку методе проверавајте флаг: ако је true — изађите. Алтернативно, можете упоредити стару и нову вредност и мењати текст само при стварном одступању.
Да, обавезно. Анонимна класа TextWatcher-а задржава референцу на Activity преко затварања. Ако listener није уклоњен, Activity не може да буде прикупљен од стране сакупљача отпада. Увек позивајте removeTextChangedListener у onDestroyView за Fragment или onDestroy за Activity.
Да, али са опрезом. У RecyclerView-у се ViewHolder-и поново користе и TextWatcher са претходне позиције може остати активан. Увек уклањајте стари TextWatcher пре постављања новог у методи onBindViewHolder. Користите ознаке или засебна поља ViewHolder-а за чување референце на listener.
За поље за претрагу користите afterTextChanged у комбинацији са debounce-ом (кашњењем). Имплементирајте тајмер од 300-500 ms који се ресетује при свакој новој промени текста. Ово спречава слање захтева на сервер при сваком притиску тастера и смањује оптерећење API-ја.
Резиме
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође