A TextWatcher egy Android interfész, amely lehetővé teszi a szövegváltozások valós idejű nyomon követését EditText és más TextView elemekben. A fejlesztő három fázisban kap értesítéseket: a változás előtt, a változás alatt és a szöveges tartalom megváltozása után. A Android Developers, 2026 szerint a TextWatcher-t a legtöbb alkalmazásban használják beviteli validációra, karakterek számlálására, autocomplete keresés implementálására és dinamikus szövegformázásra. Az interfész nélkülözhetetlen azokban az űrlapokban, ahol azonnali reakció szükséges minden egyes billentyűlenyomásra.
Főbb pontok
TextWatcher egy interfész az android.text csomagból, amely értesíti az alkalmazást az Editable objektumokban bekövetkező szövegváltozásokról. Minden egyes karakter bevitelekor, törlésekor vagy cseréjekor a TextWatcher egymás után három metódust hív meg, átadva a változások helyére vonatkozó információkat. Ez lehetővé teszi a fejlesztő számára, hogy azonnal reagáljon a felhasználói műveletekre — további gombok vagy triggerek nélkül.
A fő használati forgatókönyvek közé tartozik a mezők valós idejű validálása: e-mail ellenőrzése minden egyes karakter bevitelekor, fennmaradó karakterek számlálása egy hosszkorlátozással rendelkező mezőben, keresés implementálása késleltetett kérelemküldéssel debounce segítségével. A TextWatcher-t használják a bevitel formázására is — például szóközök automatikus elhelyezésére telefonszámban vagy maszk hozzáadására dátumhoz.
A Android Developers szerint a TextWatcher az űrlapokkal dolgozó alkalmazások 70%-ában jelen van. Az olyan könyvtárak, mint a Material Design Components és a TextInputEditText, belsőleg használják a TextWatcher-t a hibakezeléshez és a számlálók megjelenítéséhez. Ennek az interfésznek a működésének megértése elengedhetetlen minden Android-fejlesztő számára.
A TextWatcher az addTextChangedListener metóduson keresztül csatlakozik bármely TextView vagy EditText objektumhoz. Amikor a felhasználó karaktert visz be vagy töröl, az Android először a beforeTextChanged-et, majd az onTextChanged-et, végül az afterTextChanged-et hívja meg. Az egyes metódusok paramétereiben a módosított tartományra vonatkozó adatok kerülnek átadásra: kezdő pozíció, törölt karakterek száma és hozzáadott karakterek száma.
Fontos megérteni, hogy az afterTextChanged meghívása után az Editable objektum már tartalmazza az aktuális értéket. Ezért az afterTextChanged-ben kényelmes ellenőrizni a mező végleges szövegét. Addig az adatok még nincsenek teljesen frissítve. A fejlesztők gyakran összekeverik a metódusok célját, és a végső validációhoz az onTextChanged-et használják, holott a helyes választás az afterTextChanged.
Minden egyes karakter beszúrásakor, cseréjekor vagy törlésekor a hívási lánc garantáltan teljes egészében végrehajtódik. Ha azonban az afterTextChanged-en belül megváltozik a szöveg (clear, append, insert útján), a TextWatcher rekurzívan aktiválódik. Ez a StackOverflowError leggyakoribb oka az Android űrlapokban. A rekurzió megelőzésére blokkoló zászlót használnak.
Mindhárom metódus a maga szerepét játssza a szövegváltozás életciklusában. A beforeTextChanged(CharSequence s, int start, int count, int after) metódus a változtatások alkalmazása előtt hívódik meg. Átadja a karakterlánc aktuális állapotát, a változás kezdő pozícióját, a törlendő karakterek számát és a hozzáadandó karakterek számát. Itt elmenthető az előző érték, vagy ellenőrizhetők a feltételek a módosítás előtt.
Az onTextChanged metódus a változás során hívódik meg, amikor a karakterek már törlődtek, de az újak még nem kerültek beszúrásra. Paraméterek: szöveg a törlés után, kezdő pozíció, törölt karakterek száma és hozzáadandó karakterek száma. Ez a metódus animációhoz vagy naplózáshoz alkalmas, de nem az aktuális végleges szöveggel való munkához — az még nincs összeállítva.
Az afterTextChanged metódus a legkeresettebb. Kap egy Editable objektumot, és a változtatások teljes alkalmazása után hívódik meg. Ebben a metódusban lehet olvasni a mező végleges értékét, validációt végezni, frissíteni a UI-t és módosítani a szöveget (a rekurzió miatt óvatosan).
Gyakorlati példa — egy karakterszámláló egy beviteli mezőhöz, amely minden szövegváltozáskor frissül. Ilyen elem gyakran előfordul kapcsolatfelvételi űrlapokban, bejegyzésekben és hosszkorlátozott üzenetekben. A TextWatcher-en keresztüli implementáció csak néhány sort igényel, és nem kíván külső könyvtárakat.
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"
}
})
A példában az afterTextChanged metódus megkapja a mező aktuális tartalmát az s paraméteren keresztül (Editable típus). A szöveg hossza egy külön TextView-ban frissül. A rekurzió elkerülése érdekében ebben az esetben csak a counterText változik, nem maga az EditText, így nem jön létre ciklus. 200 karakteres korlátnál a túllépés után blokkolható a bevitel.
A beforeTextChanged és onTextChanged metódusok üresek maradnak, mivel a hossz számlálásához elegendő a végállapot. Ha minden változást naplózni kell, a kód hozzáadható az onTextChanged-hez. Ez a rugalmasság teszi a TextWatcher-t univerzális eszközzé bármilyen szövegbeviteli forgatókönyvhöz.
A valós idejű validáció jelentősen javítja a UX-et: a felhasználó azonnal látja a hibát a helytelen érték bevitele után, nem pedig a küldés gomb megnyomása után. A TextWatcher lehetővé teszi az e-mail, jelszó, telefonszám és más mezők azonnali ellenőrzését. Az eredmény a setError-on keresztül az EditText-ben, vagy egy külön TextView-ban hibaüzenettel jelenik meg.
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(...) {}
})
}
A példában a beépített Patterns.EMAIL_ADDRESS kerül felhasználásra az Android SDK-ból az e-mail ellenőrzéséhez. Ha a szöveg nem üres és nem egyezik a mintával, a mezőhöz az error tulajdonságon keresztül hiba kerül beállításra. Helyes bevitelkor a hiba törlődik. Fontos, hogy ne futtassuk a validációt üres mezőn — a felhasználó még nem kezdte el a bevitelt, és a hibaüzenet idő előtti lenne.
Jelszavakhoz és telefonszámokhoz egyéni reguláris kifejezéseket vagy speciális könyvtárakat használnak. Például a jelszó összetettségének ellenőrzéséhez meg lehet számolni a számjegyek, nagy- és kisbetűk számát. A TextWatcher lehetővé teszi a jelszóösszetettség-jelző valós idejű frissítését, ami pozitívan hat a regisztrációs konverzióra.
Az első és legkritikusabb hiba — a rekurzív hívás. Ha az afterTextChanged-en belül megváltozik ugyanazon EditText szövege (s.clear(), s.append() vagy s.insert() útján), a TextWatcher újra aktiválódik. Ez egy végtelen ciklust hoz létre, amely StackOverflowError-hoz vezet. Megoldás — használjunk isUpdating blokkoló zászlót, vagy ellenőrizzük, hogy a szöveg ténylegesen megváltozott-e.
A második gyakori probléma — a memóriaszivárgás. A TextWatcher egy anonim osztályon keresztül implicit hivatkozást tartalmaz az Activity-re vagy Fragment-re. Ha a listener nem kerül eltávolításra a View megsemmisülésekor, a garbage collector nem tudja felszabadítani a memóriát. Megoldás — életciklus-komponensek használata vagy az removeTextChangedListener explicit meghívása az onDestroyView-ban.
A harmadik hiba — a rossz metódus használata. Néhány fejlesztő a végső validációt az onTextChanged-ben végzi, anélkül hogy megvárná az afterTextChanged-et. Az onTextChanged-ben a szöveg még nincs teljesen frissítve, és a végleges érték olvasása helytelen adatokat eredményezhet. A helyes megközelítés — a végleges szöveg olvasásának és ellenőrzésének teljes logikáját az afterTextChanged-ben elhelyezni.
| Metódus | Hívás időpontja | Rendeltetés | Olvasható a végleges szöveg? |
|---|---|---|---|
| beforeTextChanged | A változás előtt | Előző állapot mentése | Igen |
| onTextChanged | A változás alatt | Naplózás, animáció | Nem |
| afterTextChanged | A változás után | Validáció, számlálás, UI frissítés | Igen |
A negyedik hiba — a TextWatcher többszöri hozzáadása. Ha az addTextChangedListener többször meghívásra került ugyanarra az EditText-re, az összes listener ugyanazt a változást fogja feldolgozni. A View-k dinamikus hozzáadásával működő űrlapokban ez az ellenőrzések duplikálódásához és kiszámíthatatlan viselkedéshez vezet. Mindig ellenőrizzük, hogy a listener korábban már hozzá lett-e adva, vagy használjunk egyetlen példányt.
Gyakran Ismételt Kérdések
Az OnTextChanged a szöveg változásának pillanatában hívódik meg, amikor az új karakterek még nem kerültek hozzáadásra. Ez a metódus animációhoz és naplózáshoz alkalmas. Az AfterTextChanged a változtatások teljes alkalmazása után hívódik meg, és hozzáférést biztosít a végleges szöveghez az Editable paraméteren keresztül. Validációhoz és érték olvasásához használja az afterTextChanged-et.
Használjon blokkoló zászlót (Boolean típusú), amely true-ra van állítva a szöveg afterTextChanged-en belüli módosítása előtt. A metódus elején ellenőrizze a zászlót: ha true — lépjen ki. Alternatívaként összehasonlíthatja a régi és az új értéket, és csak tényleges eltérés esetén módosíthatja a szöveget.
Igen, feltétlenül. A TextWatcher anonim osztálya egy closure-on keresztül hivatkozást tart fenn az Activity-re. Ha a listener nem kerül eltávolításra, az Activity nem gyűjthető be a garbage collector által. Mindig hívja meg a removeTextChangedListener-t az onDestroyView-ban Fragment esetén, vagy az onDestroy-ben Activity esetén.
Igen, de óvatosan. A RecyclerView-ban a ViewHolder-ok újrahasznosításra kerülnek, és az előző pozícióból származó TextWatcher aktív maradhat. Mindig távolítsa el a régi TextWatcher-t, mielőtt újat állít be az onBindViewHolder metódusban. Használjon tag-eket vagy külön ViewHolder-mezőket a listener referenciájának tárolására.
A keresőmezőhöz használja az afterTextChanged-et debounce (késleltetés) kombinációjával. Implementáljon egy 300-500 ms-os időzítőt, amely minden új szövegváltozásnál alaphelyzetbe áll. Ez megakadályozza, hogy minden egyes billentyűlenyomásnál kérés kerüljön elküldésre a szerverre, és csökkenti az API terhelését.
Összefoglaló
Kulcsrakész mobilalkalmazást fejlesztünk
Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.
Olvassa el is