SpannableString — είναι μια κλάση του Android SDK από το πακέτο android.text που επιτρέπει την εφαρμογή πολλαπλών στυλ σε διαφορετικά τμήματα μιας συμβολοσειράς κειμένου στο TextView. Σε αντίθεση με τη σήμανση HTML, το SpannableString λειτουργεί σε επίπεδο αντικειμένων Span, διαχειριζόμενο την οπτική εμφάνιση του κειμένου: χρώμα, μέγεθος, γραμματοσειρά, υπογράμμιση και διαδραστικά στοιχεία. Σύμφωνα με τους Google Developers, το SpannableString χρησιμοποιείται σε ενσωματωμένα στοιχεία του Android για μορφοποίηση συνδέσμων. Αυτός είναι ο κύριος τρόπος στυλιζαρίσματος κειμένου χωρίς σύνδεση βιβλιοθηκών τρίτων.
Κύρια σημεία
SpannableString — είναι μια κλάση Android που υλοποιεί τη διεπαφή Spannable, η οποία αποθηκεύει κείμενο μαζί με ένα σύνολο αντικειμένων Span που διαχειρίζονται την οπτική εμφάνιση. Σε αντίθεση με ένα συνηθισμένο String, το SpannableString επιτρέπει τη σύνδεση χαρακτηριστικών στυλ με συγκεκριμένες περιοχές χαρακτήρων: να κάνετε μέρος του κειμένου κόκκινο, να αυξήσετε τη γραμματοσειρά σε μια επικεφαλίδα ή να προσθέσετε έναν κλικ-σύνδεσμο μέσα σε μια παράγραφο.
Η κλάση βρίσκεται στο πακέτο android.text και είναι διαθέσιμη από το API Level 1. Το SpannableString είναι αμετάβλητο — μετά τη δημιουργία, η δομή του σταθεροποιείται και η αντικατάσταση κειμένου απαιτεί τη δημιουργία νέου αντικειμένου. Για δυναμική επεξεργασία χρησιμοποιείται το SpannableStringBuilder, το οποίο υποστηρίζει εισαγωγή και διαγραφή χαρακτήρων χωρίς απώλεια στυλ.
CharSequence — είναι η βασική διεπαφή για δεδομένα κειμένου, την οποία υλοποιούν τα String, StringBuilder και SpannableString. Η κύρια διαφορά του SpannableString από το String είναι η υποστήριξη σύνδεσης αυθαίρετων αντικειμένων σε υποσυμβολοσειρά. Το TextView αναγνωρίζει τη διεπαφή Spannable και κατά την απόδοση εφαρμόζει τα αντικείμενα span στα αντίστοιχα τμήματα κειμένου. Εάν μεταβιβάσετε ένα απλό String στο TextView, δεν θα εφαρμοστούν στυλ.
Το SpannableString αποθηκεύει το κείμενο ως πίνακα char[] και έναν ξεχωριστό πίνακα αντικειμένων span με μεταδεδομένα σχετικά με την αρχική και τελική θέση. Κατά την κλήση setSpan(what, start, end, flags), το αντικείμενο what αποθηκεύεται στη λίστα μαζί με πληροφορίες για το εύρος. Κατά την απόδοση, το TextView εφαρμόζει διαδοχικά όλα τα span που εμπίπτουν στο εμφανιζόμενο εύρος, καλώντας τις μεθόδους updateDrawState και updateMeasureState.
CharacterStyle — βασική κλάση για span που επηρεάζουν μεμονωμένους χαρακτήρες ανεξάρτητα από τη θέση τους στις γραμμές. Περιλαμβάνει ForegroundColorSpan (χρώμα κειμένου), RelativeSizeSpan (σχετικό μέγεθος), StyleSpan (έντονη και πλάγια γραφή), UnderlineSpan (υπογράμμιση) και άλλα. Τα Character span εφαρμόζονται σε κάθε χαρακτήρα στο καθορισμένο εύρος ξεχωριστά.
ParagraphStyle — διεπαφή για span που επηρεάζουν ολόκληρες παραγράφους. Ο πιο γνωστός εκπρόσωπος είναι το AlignmentSpan, το οποίο στοιχίζει ολόκληρη την παράγραφο αριστερά, κέντρο ή δεξιά. Τα Paragraph span πρέπει να καλύπτουν ολόκληρη την παράγραφο, διαφορετικά το Android αγνοεί την εφαρμογή τους. Αυτός ο περιορισμός σχετίζεται με το γεγονός ότι η στοίχιση ή η εσοχή έχει νόημα μόνο για ένα ολόκληρο μπλοκ κειμένου.
Span flags — είναι τέσσερις σταθερές που καθορίζουν τη συμπεριφορά του span κατά την εισαγωγή ή διαγραφή κειμένου στα όρια του εύρους του. Το SPAN_EXCLUSIVE_EXCLUSIVE διατηρεί το span ενεργό μόνο εντός των αρχικών ορίων, το SPAN_INCLUSIVE_INCLUSIVE το επεκτείνει όταν προστίθεται κείμενο στα όρια. Τα SPAN_EXCLUSIVE_INCLUSIVE και SPAN_INCLUSIVE_EXCLUSIVE παρέχουν μικτή συμπεριφορά για την αρχή και το τέλος του εύρους.
| Flag | Εισαγωγή αριστερά | Εισαγωγή δεξιά |
|---|---|---|
| SPAN_EXCLUSIVE_EXCLUSIVE | δεν περιλαμβάνει | δεν περιλαμβάνει |
| SPAN_INCLUSIVE_INCLUSIVE | περιλαμβάνει | περιλαμβάνει |
| SPAN_EXCLUSIVE_INCLUSIVE | δεν περιλαμβάνει | περιλαμβάνει |
| SPAN_INCLUSIVE_EXCLUSIVE | περιλαμβάνει | δεν περιλαμβάνει |
Η σωστή επιλογή flag είναι κρίσιμη για κείμενο Editable στο EditText, όπου ο χρήστης μπορεί να εισάγει και να διαγράφει χαρακτήρες. Για TextView μόνο για ανάγνωση συνήθως χρησιμοποιείται SPAN_EXCLUSIVE_EXCLUSIVE — το στυλ εφαρμόζεται μόνο στο αρχικό εύρος και δεν επεκτείνεται με προγραμματικές αλλαγές.
Το Android SDK παρέχει περισσότερες από 25 ενσωματωμένες κλάσεις span που καλύπτουν τις περισσότερες εργασίες στυλιζαρίσματος κειμένου. Κάθε κλάση υλοποιεί τη διεπαφή CharacterStyle ή ParagraphStyle και δέχεται παραμέτρους μέσω κατασκευαστή. Όλες οι κλάσεις βρίσκονται στο πακέτο android.text.style και είναι διαθέσιμες χωρίς σύνδεση πρόσθετων εξαρτήσεων.
ForegroundColorSpan ορίζει το χρώμα κειμένου για το καθορισμένο εύρος, λαμβάνοντας το χρώμα σε μορφή int. Το BackgroundColorSpan χρωματίζει το φόντο κάτω από το κείμενο, το οποίο είναι χρήσιμο για επισήμανση ερωτημάτων αναζήτησης. Το AbsoluteSizeSpan ορίζει ακριβές μέγεθος γραμματοσειράς σε pixels, το RelativeSizeSpan είναι πολλαπλασιαστής σε σχέση με το βασικό μέγεθος κειμένου στο TextView.
StyleSpan δέχεται τις σταθερές Typeface.NORMAL, Typeface.BOLD, Typeface.ITALIC ή BOLD_ITALIC και αλλάζει την όψη των χαρακτήρων. Το UnderlineSpan προσθέτει υπογράμμιση, το StrikethroughSpan διαγραφή. Τα SuperscriptSpan και SubscriptSpan δημιουργούν εκθέτες και δείκτες. Το TypefaceSpan επιτρέπει τον ορισμό προσαρμοσμένης γραμματοσειράς μέσω αντικειμένου Typeface για ένα εύρος κειμένου.
ClickableSpan — αφηρημένη κλάση για δημιουργία κλικ-τμημάτων κειμένου. Κατά το πάτημα καλείται η μέθοδος onClick(). Για να λειτουργούν τα κλικ, το TextView πρέπει να έχει setMovementMethod(LinkMovementMethod.getInstance()). Το URLSpan είναι υποκλάση του ClickableSpan για υπερσυνδέσμους με αυτόματο άνοιγμα προγράμματος περιήγησης. Το ClickableSpan συχνά συνδυάζεται με ForegroundColorSpan για οπτική επισήμανση του συνδέσμου με μπλε χρώμα.
val spannable = SpannableString("Open developer.android.com")
spannable.setSpan(
URLSpan("https://developer.android.com"),
9, 31, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)
spannable.setSpan(
ForegroundColorSpan(Color.BLUE),
9, 31, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)
textView.text = spannable
textView.movementMethod = LinkMovementMethod.getInstance()
Χωρίς LinkMovementMethod, τα κλικ στο URLSpan δεν θα υποστούν επεξεργασία. Το MovementMethod είναι υπεύθυνο για την παρακολούθηση αφής και την αναζήτηση ClickableSpan στη θέση αφής. Το χρωματιστό span κάνει τον σύνδεσμο ορατό στον χρήστη.
Η εργασία με SpannableString ξεκινά με τη δημιουργία ενός στιγμιότυπου από μια συμβολοσειρά κειμένου και τη διαδοχική εφαρμογή span μέσω της μεθόδου setSpan(). Η μέθοδος δέχεται τέσσερις παραμέτρους: αντικείμενο span, αρχική θέση, τελική θέση και flags. Αφού οριστούν όλα τα span, το αντικείμενο μεταβιβάζεται στο TextView μέσω setText().
Ας δημιουργήσουμε μια συμβολοσειρά όπου η πρώτη λέξη θα είναι κόκκινη και με αυξημένο μέγεθος. Για αυτό χρησιμοποιείται ForegroundColorSpan για το χρώμα και RelativeSizeSpan για την κλίμακα. Και τα δύο span εφαρμόζονται στο ίδιο εύρος ανεξάρτητα το ένα από το άλλο — η σειρά κλήσης setSpan δεν έχει σημασία.
val text = "Header: remaining text"
val spannable = SpannableString(text)
val colorSpan = ForegroundColorSpan(Color.RED)
val sizeSpan = RelativeSizeSpan(1.5f)
spannable.setSpan(colorSpan, 0, 9, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
spannable.setSpan(sizeSpan, 0, 9, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
textView.text = spannable
Οι χαρακτήρες από θέση 0 έως 9 λαμβάνουν και τα δύο στυλ ταυτόχρονα. Το TextView εφαρμόζει αυτόματα όλα τα span κατά την απόδοση — δεν απαιτούνται πρόσθετες κλήσεις. Το RelativeSizeSpan με πολλαπλασιαστή 1.5f αυξάνει το μέγεθος γραμματοσειράς κατά 50% σε σχέση με το βασικό.
Html.fromHtml() δημιουργεί ένα αντικείμενο Spanned από μια συμβολοσειρά HTML, αλλά το σύνολο των υποστηριζόμενων ετικετών είναι περιορισμένο. Το SpannableString παρέχει πλήρη έλεγχο σε κάθε χαρακτηριστικό χωρίς περιορισμούς HTML. Εάν απαιτείται μετατροπή HTML σε span με επακόλουθη προσθήκη προσαρμοσμένων στυλ, μπορείτε να χρησιμοποιήσετε το Html.fromHtml() ως βάση και στη συνέχεια να συμπληρώσετε με span μέσω setSpan().
val htmlText = Html.fromHtml(
"<b>Important:</b> check the data",
Html.FROM_HTML_MODE_LEGACY
)
val spannable = SpannableString(htmlText)
spannable.setSpan(
ForegroundColorSpan(Color.RED),
0, 6, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)
textView.text = spannable
Το αποτέλεσμα — το κείμενο “Σημαντικό:” θα είναι έντονο (από HTML) και κόκκινο (από span). Αυτή η προσέγγιση είναι βολική όταν εργάζεστε με περιεχόμενο διακομιστή όπου μέρος της μορφοποίησης ορίζεται από HTML και μέρος προστίθεται από την πλευρά του πελάτη προγραμματιστικά.
Το SpannableString επιτρέπει την εφαρμογή απεριόριστου αριθμού span στο ίδιο ή σε επικαλυπτόμενο εύρος. Ο συνδυασμός span είναι ένα βασικό πλεονέκτημα έναντι της σήμανσης HTML, όπου οι ένθετες ετικέτες μπορεί να συγκρούονται. Τα span είναι ανεξάρτητα και εφαρμόζονται διαδοχικά κατά την απόδοση.
Για παράδειγμα, μπορείτε να κάνετε ένα τμήμα κειμένου ταυτόχρονα έντονο, κόκκινο και κλικ. Για αυτό δημιουργούνται τρία span — StyleSpan, ForegroundColorSpan και ClickableSpan — και το καθένα εφαρμόζεται στο ίδιο εύρος. Η σειρά εφαρμογής δεν επηρεάζει το αποτέλεσμα, καθώς κάθε span είναι υπεύθυνο για το δικό του χαρακτηριστικό κειμένου.
Εάν span διαφορετικών τύπων επικαλύπτονται μερικώς, το καθένα λειτουργεί ανεξάρτητα στα όριά του. ForegroundColorSpan στο εύρος 0–10 και StyleSpan(BOLD) στο εύρος 5–15 θα δώσουν έντονο κόκκινο κείμενο στο τμήμα 5–10 και μόνο έντονο στο 10–15. Δεν προκύπτουν συγκρούσεις, καθώς κάθε span αλλάζει το δικό του χαρακτηριστικό κατά την απόδοση.
Η μέθοδος getSpans(int start, int end, Class type) επιστρέφει έναν πίνακα span που εμπίπτουν στο καθορισμένο εύρος. Αυτό είναι χρήσιμο για έλεγχο ποια στυλ έχουν ήδη εφαρμοστεί ή για διαγραφή συγκεκριμένων span. Με τη nextSpanTransition() μπορείτε να κάνετε επανάληψη στα όρια αλλαγής span — αυτό είναι η βάση της λειτουργίας προσαρμοσμένων TextView που πρέπει να γνωρίζουν πού αλλάζει το στυλ.
SpannableStringBuilder — είναι μια κλάση για σταδιακή δημιουργία στυλιζαρισμένου κειμένου με δυνατότητα εισαγωγής, αντικατάστασης και διαγραφής τμημάτων. Σε αντίθεση με το SpannableString, το οποίο δημιουργείται από μια έτοιμη συμβολοσειρά και είναι αμετάβλητο, το Builder επιτρέπει τη διαδοχική προσθήκη τμημάτων κειμένου και την εκχώρηση στυλ εν κινήσει. Αυτή είναι η ιδανική επιλογή για σύνθετα μηνύματα: αρχεία καταγραφής, συνομιλίες, τίτλους ειδήσεων με δυναμικές ετικέτες.
Το Builder υλοποιεί τις διεπαφές Spannable και Editable, καθιστώντας το συμβατό με το EditText. Ο χρήστης μπορεί να επεξεργαστεί το κείμενο, τα στυλ διατηρούνται και μετατοπίζονται σωστά κατά την εισαγωγή νέων χαρακτήρων. Το SpannableString, ως αμετάβλητο, δεν είναι κατάλληλο για επεξεργάσιμα πεδία.
Η μέθοδος append() επιστρέφει το ίδιο το builder, επιτρέποντας τη δημιουργία αλυσίδας κλήσεων. Μετά την προσθήκη κειμένου, εφαρμόζονται span μέσω setSpan(). Οι θέσεις καθορίζονται σε σχέση με το τρέχον μήκος του builder. Οι Insert() και replace() είναι επίσης διαθέσιμες για πιο ακριβή έλεγχο του περιεχομένου.
val builder = SpannableStringBuilder()
.append("New ")
.append("comment")
val blue = ForegroundColorSpan(Color.BLUE)
val gray = ForegroundColorSpan(Color.GRAY)
builder.setSpan(blue, 0, 6, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
builder.setSpan(gray, 6, 17, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
textView.text = builder
Το κείμενο “Νέο ” είναι μπλε, και το “σχόλιο” είναι γκρι. Κατά την εισαγωγή πρόσθετων χαρακτήρων μεταξύ τους, το span δεν επηρεάζει το νέο κείμενο χάρη στα flags EXCLUSIVE. Το Builder διορθώνει αυτόματα τα εσωτερικά ευρετήρια κατά την τροποποίηση.
Η χρήση μεγάλου αριθμού span επηρεάζει την απόδοση απόδοσης του TextView. Κάθε span καλεί τη μέθοδο updateDrawState() ή updateMeasureState() σε κάθε ανασχεδιασμό. Συνιστάται να περιορίζετε τον αριθμό span ανά TextView σε 50–100 για άνετη λειτουργία σε συσκευές μεσαίας κατηγορίας. Τα span που αλλάζουν το μέγεθος κειμένου (RelativeSizeSpan, AbsoluteSizeSpan) απαιτούν επαναϋπολογισμό layout σε κάθε αλλαγή, το οποίο είναι σημαντικά πιο ακριβό από span μόνο για χρώμα ή υπογράμμιση.
TextAppearanceSpan επιτρέπει την εφαρμογή ενός πλήρους συνόλου στυλ από πόρο xml android:textAppearance με μία λειτουργία setSpan(). Αντί για τρία ξεχωριστά span (χρώμα, μέγεθος, γραμματοσειρά) χρησιμοποιείται ένα TextAppearanceSpan με αναφορά στο στυλ. Αυτό μειώνει τον αριθμό αντικειμένων και απλοποιεί τη συντήρηση — η αλλαγή στυλ στον πόρο εφαρμόζεται αυτόματα σε όλα τα κείμενα που χρησιμοποιούν αυτό το span.
Η δημιουργία νέου στιγμιότυπου span για κάθε setSpan() οδηγεί σε πρόσθετο φόρτο στον συλλέκτη σκουπιδιών. Βέλτιστα είναι να δημιουργείτε σταθερά αντικείμενα span εάν εφαρμόζονται πολλές φορές. Για παράδειγμα, το ForegroundColorSpan(Color.RED) μπορεί να αποθηκευτεί σε companion object και να επαναχρησιμοποιηθεί. Ωστόσο, span με κατάσταση (ClickableSpan με διαφορετικούς χειριστές) πρέπει να δημιουργούνται ξεχωριστά για κάθε περίπτωση.
Για διάγνωση προβλημάτων απόδοσης span χρησιμοποιήστε το Layout Inspector στο Android Studio και το προφίλ GPU. Εάν το TextView με μεγάλο αριθμό span επιβραδύνεται αισθητά κατά την κύλιση, εξετάστε το ενδεχόμενο αντικατάστασης μέρους των span με στατικά στυλ μέσω TextAppearanceSpan ή μείωσης του αριθμού span συνδυάζοντας χαρακτηριστικά σε προσαρμοσμένες υλοποιήσεις UpdateAppearance.
Συχνές ερωτήσεις
String — είναι μια αμετάβλητη ακολουθία χαρακτήρων χωρίς υποστήριξη στυλ. Το SpannableString αποθηκεύει τους ίδιους χαρακτήρες, αλλά επιπλέον περιέχει έναν πίνακα αντικειμένων Span με πληροφορίες μορφοποίησης. Το TextView καθορίζει τον τύπο του CharSequence που μεταβιβάστηκε και εφαρμόζει span στα αντίστοιχα εύρη κατά την απόδοση. Το String αγνοεί οποιαδήποτε χαρακτηριστικά στυλ και εμφανίζεται ως απλό κείμενο.
Η μέθοδος removeSpan(Object span) αφαιρεί ένα συγκεκριμένο span. Για πλήρη εκκαθάριση, καλέστε getSpans(0, length, Object::class.java), η οποία επιστρέφει έναν πίνακα όλων των span, και στη συνέχεια αφαιρέστε το καθένα μέσω removeSpan. Εναλλακτικά, δημιουργήστε ένα νέο SpannableString(text.toString()) χωρίς span. Το SpannableStringBuilder έχει τη μέθοδο clear() που αφαιρεί και το κείμενο και τα span.
SpannableString λειτουργεί στο EditText, αλλά για επεξεργάσιμο κείμενο είναι προτιμότερο το SpannableStringBuilder, το οποίο υλοποιεί το Editable. Το EditText απαιτεί τη διεπαφή Editable για την παρακολούθηση αλλαγών. Εάν μεταβιβάσετε SpannableString στο EditText, το κείμενο θα εμφανιστεί με στυλ, αλλά κατά την επεξεργασία το Android θα το μετατρέψει σε Editable, το οποίο μπορεί να επαναφέρει μέρος των span.
Στο Jetpack Compose, τα span του Android SDK δεν χρησιμοποιούνται άμεσα. Αντί αυτού, το Compose παρέχει AnnotatedString — το δικό του ανάλογο του SpannableString με παρόμοιες δυνατότητες: SpanStyle για στυλ μεμονωμένων χαρακτήρων και ParagraphStyle για παραγράφους. Η μετατροπή SpannableString σε AnnotatedString είναι δυνατή μέσω buildAnnotatedString με επανάληψη στα span.
Δημιουργήστε μια κλάση που κληρονομεί από CharacterStyle και παρακάμψτε τη μέθοδο updateDrawState(TextPaint tp). Μέσα στη μέθοδο, αλλάξτε τις ιδιότητες του TextPaint: χρώμα, πάχος γραμμής, εφέ. Για μετρικές αλλαγές χρησιμοποιήστε UpdateLayout ή MetricAffectingSpan. Τα προσαρμοσμένα span εφαρμόζονται μέσω setSpan() όπως και τα ενσωματωμένα.
Σύνοψη
Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση
Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης