SpannableString is een klasse uit de Android SDK in het android.text-pakket waarmee meerdere stijlen kunnen worden toegepast op verschillende delen van een tekststring in TextView. In tegenstelling tot HTML-opmaak werkt SpannableString op het niveau van Span-objecten en beheert het de visuele weergave van tekst: kleur, grootte, lettertype, onderstreping en interactieve elementen. Volgens Google Developers wordt SpannableString gebruikt in systeemcomponenten van Android voor het formatteren van links. Het is de primaire manier om tekst te stylen zonder externe bibliotheken aan te sluiten.
Belangrijkste
SpannableString is een Android-klasse die de Spannable-interface implementeert en tekst opslaat samen met een set Span-objecten die de visuele weergave beheren. In tegenstelling tot een gewone String maakt SpannableString het mogelijk om stijlkenmerken te koppelen aan specifieke tekenbereiken: een deel van de tekst rood maken, het lettertype in de kop vergroten of een klikbare link binnen een alinea toevoegen.
De klasse bevindt zich in het pakket android.text en is beschikbaar vanaf API Level 1. SpannableString is onveranderlijk — na creatie wordt de structuur vastgelegd en het vervangen van tekst vereist het maken van een nieuw object. Voor dynamische bewerking wordt SpannableStringBuilder gebruikt, die het invoegen en verwijderen van tekens ondersteunt zonder verlies van stijlen.
CharSequence is de basisinterface voor tekstgegevens, geïmplementeerd door String, StringBuilder en SpannableString. Het belangrijkste verschil tussen SpannableString en String is de ondersteuning voor het koppelen van willekeurige objecten aan substrings. TextView herkent de Spannable-interface en past bij het renderen de span-objecten toe op de corresponderende delen van de tekst. Als een gewone String aan TextView wordt doorgegeven, worden geen stijlen toegepast.
SpannableString slaat tekst op als een array char[] en een aparte array van span-objecten met metadata over de begin- en eindpositie. Bij het aanroepen van setSpan(what, start, end, flags) wordt het object what opgeslagen in de lijst samen met informatie over het bereik. Bij het renderen past TextView achtereenvolgens alle spans toe die binnen het weergegeven bereik vallen, door de methoden updateDrawState en updateMeasureState aan te roepen.
CharacterStyle is de basisklasse voor spans die individuele tekens beïnvloeden, ongeacht hun positie in regels. Dit omvat ForegroundColorSpan (tekstkleur), RelativeSizeSpan (relatieve grootte), StyleSpan (vet en cursief), UnderlineSpan (onderstreping) en andere. Character-spans worden afzonderlijk toegepast op elk teken in het opgegeven bereik.
ParagraphStyle is de interface voor spans die hele alinea’s beïnvloeden. De bekendste vertegenwoordiger is AlignmentSpan, die de hele alinea links, gecentreerd of rechts uitlijnt. Paragraph-spans moeten de hele alinea bestrijken, anders negeert Android hun toepassing. Deze beperking komt doordat uitlijning of inspringing alleen zinvol is voor een volledig tekstblok.
Span flags zijn vier constanten die het gedrag van de span bepalen bij het invoegen of verwijderen van tekst op de grenzen van het bereik. SPAN_EXCLUSIVE_EXCLUSIVE houdt de span alleen actief binnen de oorspronkelijke grenzen, SPAN_INCLUSIVE_INCLUSIVE breidt deze uit bij het toevoegen van tekst aan de grenzen. SPAN_EXCLUSIVE_INCLUSIVE en SPAN_INCLUSIVE_EXCLUSIVE bieden gemengd gedrag voor het begin en einde van het bereik.
| Flag | Invoegen links | Invoegen rechts |
|---|---|---|
| SPAN_EXCLUSIVE_EXCLUSIVE | sluit niet in | sluit niet in |
| SPAN_INCLUSIVE_INCLUSIVE | sluit in | sluit in |
| SPAN_EXCLUSIVE_INCLUSIVE | sluit niet in | sluit in |
| SPAN_INCLUSIVE_EXCLUSIVE | sluit in | sluit niet in |
De juiste keuze van de flag is kritisch voor Editable-tekst in EditText, waar de gebruiker tekens kan invoegen en verwijderen. Voor alleen-lezen TextView wordt meestal SPAN_EXCLUSIVE_EXCLUSIVE gebruikt — de stijl wordt alleen toegepast op het oorspronkelijke bereik en wordt niet uitgebreid bij programmatische wijzigingen.
Android SDK biedt meer dan 25 ingebouwde klassen van spans die de meeste tekststylingtaken dekken. Elke klasse implementeert de interface CharacterStyle of ParagraphStyle en accepteert parameters via de constructor. Alle klassen bevinden zich in het pakket android.text.style en zijn beschikbaar zonder extra afhankelijkheden aan te sluiten.
ForegroundColorSpan stelt de tekstkleur in voor het opgegeven bereik, met kleur in int-formaat. BackgroundColorSpan kleurt de achtergrond onder de tekst, handig voor het markeren van zoekopdrachten. AbsoluteSizeSpan stelt de exacte lettergrootte in pixels in, RelativeSizeSpan — een vermenigvuldiger ten opzichte van de basislettergrootte van de tekst in TextView.
StyleSpan accepteert de constanten Typeface.NORMAL, Typeface.BOLD, Typeface.ITALIC of BOLD_ITALIC en wijzigt het uiterlijk van tekens. UnderlineSpan voegt onderstreping toe, StrikethroughSpan — doorhaling. SuperscriptSpan en SubscriptSpan maken superscript en subscript. TypefaceSpan maakt het mogelijk om een aangepast lettertype in te stellen via een Typeface-object voor een tekstbereik.
ClickableSpan is een abstracte klasse voor het maken van klikbare tekstgedeelten. Bij indrukken wordt de methode onClick() aangeroepen. Om klikken te laten werken, moet TextView setMovementMethod(LinkMovementMethod.getInstance()) hebben. URLSpan is een subklasse van ClickableSpan voor hyperlinks met automatisch openen van de browser. ClickableSpan wordt vaak gecombineerd met ForegroundColorSpan om de link visueel te markeren met blauw.
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()
Zonder LinkMovementMethod worden klikken op URLSpan niet verwerkt. MovementMethod is verantwoordelijk voor het onderscheppen van aanraakgebeurtenissen en het zoeken naar ClickableSpan op de aanraakpositie. De gekleurde span maakt de link zichtbaar voor de gebruiker.
Werken met SpannableString begint met het maken van een instantie uit een tekststring en het achtereenvolgens toepassen van spans via de methode setSpan(). De methode accepteert vier parameters: het span-object, de beginpositie, de eindpositie en de flags. Na het instellen van alle spans wordt het object doorgegeven aan TextView via setText().
We maken een string waarbij het eerste woord rood en vergroot is. Hiervoor wordt ForegroundColorSpan voor kleur en RelativeSizeSpan voor schaal gebruikt. Beide spans worden onafhankelijk van elkaar op hetzelfde bereik toegepast — de volgorde van setSpan-aanroepen doet er niet toe.
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
Tekens van positie 0 tot 9 krijgen beide stijlen tegelijk. TextView past automatisch alle spans toe bij het renderen — er zijn geen extra aanroepen nodig. RelativeSizeSpan met een vermenigvuldiger van 1.5f vergroot het lettertype met 50% ten opzichte van de basis.
Html.fromHtml() maakt een Spanned-object uit een HTML-string, maar de set ondersteunde tags is beperkt. SpannableString geeft volledige controle over elk attribuut zonder de beperkingen van HTML. Als HTML moet worden omgezet in spans met het toevoegen van aangepaste stijlen, kan Html.fromHtml() als basis worden gebruikt, waarna wordt aangevuld met spans via 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
Het resultaat — de tekst „Belangrijk:“ zal vet (uit HTML) en rood (uit de span) zijn. Deze benadering is handig bij het werken met servercontent, waar een deel van de opmaak via HTML wordt ingesteld en een deel programmatisch aan de clientzijde wordt toegevoegd.
SpannableString maakt het mogelijk om een onbeperkt aantal spans toe te passen op hetzelfde of overlappende bereik. Het combineren van spans is een belangrijk voordeel ten opzichte van HTML-opmaak, waar geneste tags kunnen conflicteren. Spans zijn onafhankelijk en worden achtereenvolgens toegepast bij het renderen.
Bijvoorbeeld, een tekstgedeelte kan tegelijkertijd vet, rood en klikbaar worden gemaakt. Hiervoor worden drie spans gemaakt — StyleSpan, ForegroundColorSpan en ClickableSpan — en elk wordt toegepast op hetzelfde bereik. De volgorde van toepassing heeft geen invloed op het resultaat, omdat elke span verantwoordelijk is voor zijn eigen teksteigenschap.
Als spans van verschillende typen gedeeltelijk overlappen, werkt elk onafhankelijk binnen zijn eigen grenzen. ForegroundColorSpan op bereik 0-10 en StyleSpan(BOLD) op bereik 5-15 geven rode vette tekst op het gedeelte 5-10 en alleen vet op 10-15. Er ontstaan geen conflicten, omdat elke span zijn eigen attribuut wijzigt bij het renderen.
De methode getSpans(int start, int end, Class type) retourneert een array van spans die binnen het opgegeven bereik vallen. Dit is handig om te controleren welke stijlen zijn toegepast of om specifieke spans te verwijderen. Met nextSpanTransition() kan worden geïtereerd over de grenzen van spanwijzigingen — dit is de basis van aangepaste TextView-objecten die moeten weten waar de stijl verandert.
SpannableStringBuilder is een klasse voor het stapsgewijs bouwen van gestileerde tekst met de mogelijkheid om fragmenten in te voegen, te vervangen en te verwijderen. In tegenstelling tot SpannableString, die uit een kant-en-klare string wordt gemaakt en onveranderlijk is, maakt Builder het mogelijk om tekstgedeelten achtereenvolgens toe te voegen en stijlen onderweg toe te kennen. Dit is de ideale keuze voor samengestelde berichten: logs, chats, nieuwsheaders met dynamische labels.
Builder implementeert de interfaces Spannable en Editable, wat het compatibel maakt met EditText. De gebruiker kan de tekst bewerken en de stijlen blijven behouden en worden correct verschoven bij het invoegen van nieuwe tekens. SpannableString, dat onveranderlijk is, is niet geschikt voor bewerkbare velden.
De methode append() retourneert de builder zelf, waardoor een keten van aanroepen kan worden opgebouwd. Na het toevoegen van tekst worden spans toegepast via setSpan(). Posities worden aangegeven ten opzichte van de huidige lengte van de builder. Insert() en replace() zijn ook beschikbaar voor nauwkeurigere controle over de inhoud.
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
De tekst „Nieuw “ is blauw gekleurd en „reactie“ grijs. Bij het invoegen van extra tekens ertussen, zal de span de nieuwe tekst niet beïnvloeden vanwege de EXCLUSIVE-flags. Builder corrigeert automatisch de interne indices bij wijzigingen.
Het gebruik van een groot aantal spans beïnvloedt de renderingprestaties van TextView. Elke span roept de methode updateDrawState() of updateMeasureState() aan bij elke herrendering. Het wordt aanbevolen om het aantal spans per TextView te beperken tot 50–100 voor comfortabel werken op middensegment apparaten. Spans die de tekstgrootte wijzigen (RelativeSizeSpan, AbsoluteSizeSpan) vereisen een herberekening van de layout bij elke wijziging, wat aanzienlijk duurder is dan spans alleen voor kleur of onderstreping.
TextAppearanceSpan maakt het mogelijk om een volledige set stijlen uit een xml-bron android:textAppearance toe te passen met een enkele setSpan()-bewerking. In plaats van drie afzonderlijke spans (kleur, grootte, lettertype) wordt een enkele TextAppearanceSpan met een verwijzing naar de stijl gebruikt. Dit vermindert het aantal objecten en vereenvoudigt het onderhoud — een wijziging van de stijl in de bron wordt automatisch toegepast op alle teksten die deze span gebruiken.
Het maken van een nieuw exemplaar van een span voor elke setSpan() leidt tot extra belasting van de garbage collector. Optimaal is het maken van constante objecten van spans als ze herhaaldelijk worden toegepast. Bijvoorbeeld, ForegroundColorSpan(Color.RED) kan worden opgeslagen in een companion object en worden hergebruikt. Echter, spans met toestand (ClickableSpan met verschillende handlers) moeten voor elk geval afzonderlijk worden gemaakt.
Voor het diagnosticeren van prestatieproblemen met spans gebruikt u Layout Inspector in Android Studio en de GPU-profiler. Als TextView met een groot aantal spans merkbaar vertraagt bij scrollen, overweeg dan om een deel van de spans te vervangen door statische stijlen via TextAppearanceSpan of het aantal spans te verminderen door attributen te combineren in aangepaste UpdateAppearance-implementaties.
Veelgestelde vragen
String is een onveranderlijke tekenreeks zonder ondersteuning voor stijlen. SpannableString slaat dezelfde tekens op, maar bevat daarnaast een array van Span-objecten met opmaakinformatie. TextView bepaalt het type van de doorgegeven CharSequence en past de spans toe op de corresponderende bereiken bij het renderen. String negeert alle stijlkenmerken en wordt weergegeven als platte tekst.
De methode removeSpan(Object span) verwijdert een specifieke span. Voor volledige opschoning roept u getSpans(0, length, Object::class.java) aan, die een array van alle spans retourneert, en verwijdert u vervolgens elke span via removeSpan. Als alternatief maakt u een nieuwe SpannableString(text.toString()) zonder spans. SpannableStringBuilder heeft een methode clear() die zowel de tekst als de spans verwijdert.
SpannableString werkt in EditText, maar voor bewerkbare tekst heeft SpannableStringBuilder de voorkeur, die Editable implementeert. EditText vereist de Editable-interface om wijzigingen bij te houden. Als SpannableString aan EditText wordt doorgegeven, wordt de tekst met stijlen weergegeven, maar bij bewerken converteert Android deze naar Editable, wat een deel van de spans kan resetten.
In Jetpack Compose worden Android SDK-spans niet direct gebruikt. In plaats daarvan biedt Compose AnnotatedString — zijn eigen equivalent van SpannableString met vergelijkbare mogelijkheden: SpanStyle voor stijlen van individuele tekens en ParagraphStyle voor alinea’s. Conversie van SpannableString naar AnnotatedString is mogelijk via buildAnnotatedString met iteratie over de spans.
Maak een klasse die overerft van CharacterStyle en overschrijf de methode updateDrawState(TextPaint tp). In de methode wijzigt u de eigenschappen van TextPaint: kleur, lijndikte, effecten. Voor metrische wijzigingen gebruikt u UpdateLayout of MetricAffectingSpan. Aangepaste spans worden toegepast via setSpan() net als ingebouwde spans.
Samenvatting
We ontwikkelen een mobiele applicatie turnkey
IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.
Lees ook