Το TextWatcher είναι μια διεπαφή Android που σας επιτρέπει να παρακολουθείτε αλλαγές κειμένου σε EditText και άλλα TextView σε πραγματικό χρόνο. Ο προγραμματιστής λαμβάνει ειδοποιήσεις σε τρία στάδια: πριν από την αλλαγή, κατά τη διάρκεια της αλλαγής και μετά την αλλαγή του περιεχομένου κειμένου. Σύμφωνα με το Android Developers, 2026, το TextWatcher χρησιμοποιείται στις περισσότερες εφαρμογές για επικύρωση εισόδου, μέτρηση χαρακτήρων, υλοποίηση αναζήτησης με autocomplete και δυναμική μορφοποίηση κειμένου. Η διεπαφή είναι απαραίτητη σε φόρμες όπου απαιτείται άμεση αντίδραση σε κάθε πάτημα πλήκτρου.
Κύρια σημεία
TextWatcher είναι μια διεπαφή από το πακέτο android.text που ειδοποιεί την εφαρμογή για αλλαγές κειμένου σε αντικείμενα Editable. Σε κάθε είσοδο, διαγραφή ή αντικατάσταση χαρακτήρα, το TextWatcher καλεί διαδοχικά τρεις μεθόδους, μεταφέροντας πληροφορίες σχετικά με τη θέση των αλλαγών. Αυτό επιτρέπει στον προγραμματιστή να αντιδρά άμεσα στις ενέργειες του χρήστη — χωρίς επιπλέον κουμπιά ή ενεργοποιητές.
Τα κύρια σενάρια χρήσης περιλαμβάνουν επικύρωση πεδίων σε πραγματικό χρόνο: έλεγχος email κατά την εισαγωγή κάθε χαρακτήρα, μέτρηση υπολοίπων χαρακτήρων σε πεδίο με όριο μήκους, υλοποίηση αναζήτησης με καθυστερημένη αποστολή αιτήματος μέσω 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 ένα καθολικό εργαλείο για οποιοδήποτε σενάριο εργασίας με είσοδο κειμένου.
Η επικύρωση σε πραγματικό χρόνο βελτιώνει σημαντικά την εμπειρία χρήστη: ο χρήστης βλέπει το σφάλμα αμέσως μετά την εισαγωγή μιας λανθασμένης τιμής, όχι αφού πατήσει το κουμπί αποστολής. Το TextWatcher επιτρέπει τον άμεσο έλεγχο email, κωδικού πρόσβασης, αριθμού τηλεφώνου και άλλων πεδίων. Το αποτέλεσμα εμφανίζεται μέσω 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 για έλεγχο email. Εάν το κείμενο δεν είναι κενό και δεν ταιριάζει με το πρότυπο, ορίζεται σφάλμα στο πεδίο μέσω της ιδιότητας error. Στη σωστή εισαγωγή, το σφάλμα διαγράφεται. Είναι σημαντικό να μην εκτελείτε επικύρωση σε κενό πεδίο — ο χρήστης μπορεί να μην έχει αρχίσει ακόμη την εισαγωγή και το μήνυμα σφάλματος θα είναι πρόωρο.
Για κωδικούς πρόσβασης και αριθμούς τηλεφώνου χρησιμοποιούνται προσαρμοσμένες κανονικές εκφράσεις ή εξειδικευμένες βιβλιοθήκες. Για παράδειγμα, για τον έλεγχο πολυπλοκότητας κωδικού πρόσβασης μπορείτε να μετρήσετε τον αριθμό ψηφίων, κεφαλαίων και πεζών γραμμάτων. Το TextWatcher επιτρέπει την ενημέρωση του δείκτη πολυπλοκότητας κωδικού πρόσβασης σε πραγματικό χρόνο, το οποίο επηρεάζει θετικά τη μετατροπή εγγραφής.
Το πρώτο και πιο κρίσιμο λάθος — αναδρομική κλήση. Εάν μέσα στο afterTextChanged αλλάξει το κείμενο του ίδιου EditText (μέσω s.clear(), s.append() ή s.insert()), το TextWatcher θα ενεργοποιηθεί ξανά. Αυτό δημιουργεί έναν ατελείωτο βρόχο που καταλήγει σε StackOverflowError. Λύση — χρήση σημαίας αποκλεισμού isUpdating ή έλεγχος εάν το κείμενο άλλαξε πραγματικά.
Το δεύτερο συνηθισμένο πρόβλημα — διαρροή μνήμης. Το TextWatcher περιέχει μια σιωπηρή αναφορά στο Activity ή Fragment μέσω μιας ανώνυμης κλάσης. Εάν ο listener δεν αφαιρεθεί κατά την καταστροφή του View, ο garbage collector δεν μπορεί να ελευθερώσει τη μνήμη. Λύση — χρήση στοιχείων κύκλου ζωής ή ρητή κλήση του removeTextChangedListener στο onDestroyView.
Το τρίτο λάθος — χρήση λανθασμένης μεθόδου. Μερικοί προγραμματιστές εκτελούν την τελική επικύρωση στο onTextChanged, χωρίς να περιμένουν το afterTextChanged. Στο onTextChanged, το κείμενο δεν έχει ακόμη ενημερωθεί πλήρως και η ανάγνωση της τελικής τιμής μπορεί να επιστρέψει λανθασμένα δεδομένα. Η σωστή προσέγγιση — τοποθέτηση όλης της λογικής ανάγνωσης και ελέγχου του τελικού κειμένου στο afterTextChanged.
| Μέθοδος | Στιγμή κλήσης | Σκοπός | Μπορεί να διαβαστεί το τελικό κείμενο; |
|---|---|---|---|
| beforeTextChanged | Πριν την αλλαγή | Αποθήκευση προηγούμενης κατάστασης | Ναι |
| onTextChanged | Κατά την αλλαγή | Καταγραφή, κινούμενα σχέδια | Όχι |
| afterTextChanged | Μετά την αλλαγή | Επικύρωση, μέτρηση, ενημέρωση UI | Ναι |
Το τέταρτο λάθος — πολλαπλή προσθήκη TextWatcher. Εάν το addTextChangedListener κλήθηκε πολλές φορές για το ίδιο EditText, όλοι οι listeners θα επεξεργαστούν την ίδια αλλαγή. Σε φόρμες με δυναμική προσθήκη View, αυτό οδηγεί σε διπλασιασμό ελέγχων και απρόβλεπτη συμπεριφορά. Πάντα ελέγχετε εάν ο listener έχει ήδη προστεθεί προηγουμένως ή χρησιμοποιήστε ένα μοναδικό στιγμιότυπο.
Συχνές Ερωτήσεις
To OnTextChanged καλείται τη στιγμή της αλλαγής κειμένου, όταν οι νέοι χαρακτήρες δεν έχουν ακόμη προστεθεί. Αυτή η μέθοδος είναι κατάλληλη για κινούμενα σχέδια και καταγραφή. Το AfterTextChanged καλείται μετά την πλήρη εφαρμογή των αλλαγών και παρέχει πρόσβαση στο τελικό κείμενο μέσω της παραμέτρου Editable. Για επικύρωση και ανάγνωση τιμής, χρησιμοποιήστε το afterTextChanged.
Χρησιμοποιήστε μια σημαία αποκλεισμού τύπου Boolean που ορίζεται σε true πριν από την αλλαγή κειμένου μέσα στο afterTextChanged. Στην αρχή της μεθόδου ελέγξτε τη σημαία: εάν είναι true — βγείτε. Εναλλακτικά, μπορείτε να συγκρίνετε την παλιά και τη νέα τιμή και να αλλάξετε το κείμενο μόνο όταν υπάρχει πραγματική διαφορά.
Ναι, υποχρεωτικά. Η ανώνυμη κλάση του TextWatcher διατηρεί μια αναφορά στο Activity μέσω του closure. Εάν ο listener δεν αφαιρεθεί, το Activity δεν μπορεί να συλλεχθεί από τον garbage collector. Πάντα καλείτε το removeTextChangedListener στο onDestroyView για Fragment ή στο onDestroy για Activity.
Ναι, αλλά με προσοχή. Στο RecyclerView, τα ViewHolder επαναχρησιμοποιούνται και το TextWatcher από την προηγούμενη θέση μπορεί να παραμείνει ενεργό. Πάντα αφαιρείτε το παλιό TextWatcher πριν ορίσετε ένα νέο στη μέθοδο onBindViewHolder. Χρησιμοποιήστε tags ή ξεχωριστά πεδία ViewHolder για αποθήκευση αναφοράς στον listener.
Για το πεδίο αναζήτησης χρησιμοποιήστε το afterTextChanged σε συνδυασμό με debounce (καθυστέρηση). Εφαρμόστε ένα χρονόμετρο 300-500 ms που γίνεται reset σε κάθε νέα αλλαγή κειμένου. Αυτό αποτρέπει την αποστολή αιτήματος στον διακομιστή σε κάθε πάτημα πλήκτρου και μειώνει το φορτίο του API.
Σύνοψη
Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση
Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης