SpannableString : définition, classes de style et fonctionnement

Auteur : IT Sectr Publié le : 2026-07-23 Temps de lecture : 10 min

SpannableString est une classe Android SDK du paquet android.text qui permet d'appliquer plusieurs styles à différentes parties d'une même chaîne de texte dans TextView. Contrairement au balisage HTML, SpannableString travaille au niveau des objets Span, contrôlant l'affichage visuel du texte : couleur, taille, police, soulignement et éléments interactifs. Selon Google Developers, SpannableString est utilisé dans les composants système Android pour formater les liens. C'est la principale façon de styliser du texte sans utiliser de bibliothèques tierces.

Points clés

  • SpannableString — une classe Android pour styliser du texte via des objets Span, immuable après création
  • Les spans se divisent en CharacterStyle (caractères individuels) et ParagraphStyle (paragraphes entiers)
  • ForegroundColorSpan change la couleur du texte, StyleSpan gère le gras et l'italique
  • SpannableStringBuilder — une version mutable pour la construction dynamique de texte avec support Editable
  • Les flags de Span déterminent le comportement du span lors de l'insertion ou la suppression de texte aux limites de la plage

Qu'est-ce que SpannableString ?

SpannableString est une classe Android qui implémente l'interface Spannable, stockant du texte avec un ensemble d'objets Span qui contrôlent l'affichage visuel. Contrairement à une String classique, SpannableString permet d'attacher des attributs de style à des plages spécifiques de caractères : rendre une partie du texte rouge, augmenter la taille de la police dans un titre ou ajouter un lien cliquable dans un paragraphe.

La classe se trouve dans le paquet android.text et est disponible depuis l'API Level 1. SpannableString est immuable — une fois créée, sa structure est figée et le remplacement du texte nécessite la création d'un nouvel objet. Pour l'édition dynamique, on utilise SpannableStringBuilder, qui supporte l'insertion et la suppression de caractères sans perdre les styles.

Différence avec CharSequence

CharSequence est l'interface de base pour les données textuelles, implémentée par String, StringBuilder et SpannableString. La principale différence entre SpannableString et String est le support pour attacher des objets arbitraires à une sous-chaîne. TextView reconnaît l'interface Spannable et applique les objets span aux segments de texte correspondants lors du rendu. Si vous passez une String classique à TextView, aucun style ne sera appliqué.

Structure interne

SpannableString stocke le texte sous forme de tableau char[] et un tableau séparé d'objets span avec des métadonnées sur les positions de début et de fin. Lorsque setSpan(what, start, end, flags) est appelé, l'objet what est sauvegardé dans la liste avec les informations de plage. Lors du rendu, TextView applique séquentiellement tous les spans qui se trouvent dans la plage affichée, appelant les méthodes updateDrawState et updateMeasureState.

Types de spans : CharacterStyle et ParagraphStyle

CharacterStyle est la classe de base pour les spans qui affectent des caractères individuels indépendamment de leur position dans les lignes. Cela inclut ForegroundColorSpan (couleur du texte), RelativeSizeSpan (taille relative), StyleSpan (gras et italique), UnderlineSpan (soulignement) et autres. Les spans de caractères sont appliqués à chaque caractère de la plage spécifiée individuellement.

ParagraphStyle est une interface pour les spans qui affectent des paragraphes entiers. Le représentant le plus connu est AlignmentSpan, qui aligne tout le paragraphe à gauche, au centre ou à droite. Les spans de paragraphe doivent couvrir l'intégralité du paragraphe ; sinon, Android ignore leur application. Cette limitation est due au fait que l'alignement ou le retrait n'a de sens que pour un bloc de texte complet.

Flags de Span

Les flags de Span sont quatre constantes qui définissent le comportement du span lors de l'insertion ou de la suppression de texte aux limites de sa plage. SPAN_EXCLUSIVE_EXCLUSIVE maintient le span actif uniquement dans les limites d'origine, SPAN_INCLUSIVE_INCLUSIVE l'étend lors de l'ajout de texte aux limites. SPAN_EXCLUSIVE_INCLUSIVE et SPAN_INCLUSIVE_EXCLUSIVE offrent un comportement mixte pour le début et la fin de la plage.

FlagInsertion à gaucheInsertion à droite
SPAN_EXCLUSIVE_EXCLUSIVEn'inclut pasn'inclut pas
SPAN_INCLUSIVE_INCLUSIVEinclutinclut
SPAN_EXCLUSIVE_INCLUSIVEn'inclut pasinclut
SPAN_INCLUSIVE_EXCLUSIVEinclutn'inclut pas

Choisir le bon flag est crucial pour le texte Editable dans EditText, où l'utilisateur peut insérer et supprimer des caractères. Pour TextView en lecture seule, on utilise généralement SPAN_EXCLUSIVE_EXCLUSIVE — le style s'applique uniquement à la plage d'origine et ne s'étend pas avec les modifications programmatiques.

Principales classes de spans dans Android SDK

Le SDK Android fournit plus de 25 classes de spans intégrées couvrant la plupart des tâches de style de texte. Chaque classe implémente l'interface CharacterStyle ou ParagraphStyle et accepte des paramètres via son constructeur. Toutes les classes sont dans le paquet android.text.style et sont disponibles sans ajout de dépendances supplémentaires.

Spans de couleur et d'arrière-plan

ForegroundColorSpan définit la couleur du texte pour une plage spécifiée, acceptant une couleur au format int. BackgroundColorSpan colore l'arrière-plan derrière le texte, utile pour surligner des résultats de recherche. AbsoluteSizeSpan définit une taille de police exacte en pixels, RelativeSizeSpan applique un multiplicateur par rapport à la taille de texte de base dans TextView.

Spans de police

StyleSpan accepte les constantes Typeface.NORMAL, Typeface.BOLD, Typeface.ITALIC ou BOLD_ITALIC et modifie la police des caractères. UnderlineSpan ajoute un soulignement, StrikethroughSpan ajoute un barré. SuperscriptSpan et SubscriptSpan créent des exposants et des indices. TypefaceSpan permet de définir une police personnalisée via un objet Typeface pour une plage de texte.

Spans interactifs

ClickableSpan est une classe abstraite pour créer des segments de texte cliquables. Au toucher, la méthode onClick() est appelée. Pour que les clics fonctionnent, TextView doit avoir setMovementMethod(LinkMovementMethod.getInstance()). URLSpan est une sous-classe de ClickableSpan pour les hyperliens avec ouverture automatique du navigateur. ClickableSpan est souvent combiné avec ForegroundColorSpan pour mettre visuellement en évidence le lien en bleu.

kotlin
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()

Sans LinkMovementMethod, les clics sur URLSpan ne seront pas traités. MovementMethod est responsable de l'interception des événements tactiles et de la recherche du ClickableSpan à la position du toucher. Le span de couleur rend le lien visible pour l'utilisateur.

Comment utiliser SpannableString dans le code

Travailler avec SpannableString commence par créer une instance à partir d'une chaîne de texte et appliquer séquentiellement des spans via la méthode setSpan(). La méthode accepte quatre paramètres : l'objet span, la position de début, la position de fin et les flags. Après avoir défini tous les spans, l'objet est passé à TextView via setText().

Couleur et taille du texte

Créons une chaîne où le premier mot est rouge et agrandi. Pour cela, nous utilisons ForegroundColorSpan pour la couleur et RelativeSizeSpan pour l'échelle. Les deux spans sont appliqués à la même plage indépendamment — l'ordre des appels setSpan n'a pas d'importance.

kotlin
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

Les caractères de la position 0 à 9 reçoivent les deux styles simultanément. TextView applique automatiquement tous les spans lors du rendu — aucun appel supplémentaire n'est nécessaire. RelativeSizeSpan avec un multiplicateur de 1,5f augmente la taille de la police de 50% par rapport à la base.

Combinaison avec les balises HTML

Html.fromHtml() crée un objet Spanned à partir d'une chaîne HTML, mais l'ensemble des balises supportées est limité. SpannableString donne un contrôle total sur chaque attribut sans les limitations du HTML. Si vous devez convertir du HTML en spans puis ajouter des styles personnalisés, vous pouvez utiliser Html.fromHtml() comme base, puis compléter avec des spans via setSpan().

kotlin
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

Le résultat — le texte « Important : » sera en gras (du HTML) et rouge (du span). Cette approche est pratique lors du travail avec du contenu serveur où une partie du formatage est spécifiée en HTML et une autre est ajoutée côté client par programmation.

Combinaison de plusieurs spans

SpannableString permet d'appliquer un nombre illimité de spans à la même plage ou à des plages chevauchantes. Combiner des spans est un avantage clé par rapport au balisage HTML, où les balises imbriquées peuvent entrer en conflit. Les spans sont indépendants et appliqués séquentiellement lors du rendu.

Par exemple, vous pouvez rendre un segment de texte simultanément gras, rouge et cliquable. Pour ce faire, créez trois spans — StyleSpan, ForegroundColorSpan et ClickableSpan — et appliquez chacun à la même plage. L'ordre d'application n'affecte pas le résultat, car chaque span est responsable de son propre attribut de texte.

Chevauchement de spans de différents types

Si des spans de différents types se chevauchent partiellement, chacun fonctionne indépendamment dans ses propres limites. ForegroundColorSpan sur la plage 0–10 et StyleSpan(BOLD) sur la plage 5–15 donneront du texte rouge gras sur le segment 5–10 et seulement gras sur 10–15. Aucun conflit ne survient car chaque span modifie son propre attribut lors du rendu.

Obtenir la liste des spans

La méthode getSpans(int start, int end, Class type) retourne un tableau des spans dans la plage spécifiée. C'est utile pour vérifier quels styles sont déjà appliqués ou pour supprimer des spans spécifiques. Avec nextSpanTransition(), vous pouvez itérer sur les limites de changement des spans — c'est la base des implémentations personnalisées de TextView qui doivent savoir où le style change.

SpannableStringBuilder pour la construction de texte

SpannableStringBuilder est une classe pour la construction étape par étape de texte stylisé avec la possibilité d'insérer, remplacer et supprimer des fragments. Contrairement à SpannableString, qui est créé à partir d'une chaîne prête et est immuable, Builder permet d'ajouter des parties de texte séquentiellement et d'assigner des styles à la volée. C'est idéal pour les messages composites : logs, chats, titres d'actualités avec des étiquettes dynamiques.

Builder implémente les interfaces Spannable et Editable, ce qui le rend compatible avec EditText. L'utilisateur peut éditer le texte, et les styles sont préservés et correctement décalés lors de l'insertion de nouveaux caractères. SpannableString, étant immuable, ne convient pas aux champs éditables.

Chaînage de méthodes

La méthode append() retourne le builder lui-même, permettant le chaînage de méthodes. Après l'ajout de texte, les spans sont appliqués via setSpan(). Les positions sont spécifiées par rapport à la longueur actuelle du builder. Insert() et replace() sont également disponibles pour un contrôle plus précis du contenu.

kotlin
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

Le texte « Nouveau » est coloré en bleu, et « commentaire » en gris. Lors de l'insertion de caractères supplémentaires entre eux, le span n'affectera pas le nouveau texte grâce aux flags EXCLUSIVE. Builder ajuste automatiquement les index internes lors de la modification.

Performances et optimisation des spans

L'utilisation d'un grand nombre de spans affecte les performances de rendu de TextView. Chaque span appelle la méthode updateDrawState() ou updateMeasureState() à chaque redessin. Il est recommandé de limiter le nombre de spans par TextView à 50–100 pour des performances confortables sur les appareils de milieu de gamme. Les spans qui modifient la taille du texte (RelativeSizeSpan, AbsoluteSizeSpan) nécessitent un recalcul de la mise en page à chaque changement, ce qui est nettement plus coûteux que les spans de couleur ou de soulignement uniquement.

TextAppearanceSpan

TextAppearanceSpan permet d'appliquer un ensemble complet de styles à partir d'une ressource XML android:textAppearance avec un seul appel setSpan(). Au lieu de trois spans séparés (couleur, taille, police), un seul TextAppearanceSpan avec une référence de style est utilisé. Cela réduit le nombre d'objets et simplifie la maintenance — modifier le style dans la ressource s'applique automatiquement à tous les textes utilisant ce span.

Réutilisation d'objets

Créer une nouvelle instance de span pour chaque setSpan() ajoute une charge supplémentaire au ramasse-miettes. Il est optimal de créer des objets span constants s'ils sont utilisés de manière répétée. Par exemple, ForegroundColorSpan(Color.RED) peut être stocké dans un objet companion et réutilisé. Cependant, les spans avec état (ClickableSpan avec différents gestionnaires) doivent être créés individuellement pour chaque cas.

Mesure et profilage

Pour diagnostiquer les problèmes de performance des spans, utilisez Layout Inspector dans Android Studio et le profileur GPU. Si un TextView avec de nombreux spans ralentit notablement lors du défilement, envisagez de remplacer certains spans par des styles statiques via TextAppearanceSpan ou de réduire le nombre de spans en combinant des attributs dans des implémentations personnalisées d'UpdateAppearance.

Foire aux questions

En quoi SpannableString diffère-t-il d'une String classique ?

String est une séquence immuable de caractères sans support de style. SpannableString stocke les mêmes caractères mais contient en plus un tableau d'objets Span avec des informations de formatage. TextView détermine le type de CharSequence passé et applique les spans aux plages correspondantes lors du rendu. String ignore tous les attributs de style et s'affiche en texte brut.

Comment supprimer tous les spans d'un SpannableString ?

La méthode removeSpan(Object span) supprime un span spécifique. Pour un nettoyage complet, appelez getSpans(0, length, Object::class.java), qui retourne un tableau de tous les spans, puis supprimez chacun via removeSpan. Alternativement, créez un nouveau SpannableString(text.toString()) sans spans. SpannableStringBuilder a une méthode clear() qui supprime à la fois le texte et les spans.

SpannableString fonctionne-t-il dans EditText ?

SpannableString fonctionne dans EditText, mais pour du texte modifiable, SpannableStringBuilder qui implémente Editable est préférable. EditText nécessite l'interface Editable pour suivre les modifications. Si vous passez SpannableString à EditText, le texte s'affichera avec les styles, mais lors de l'édition, Android le convertira en Editable, ce qui peut réinitialiser certains spans.

Peut-on utiliser SpannableString dans Compose ?

Dans Jetpack Compose, les spans du SDK Android ne sont pas utilisés directement. À la place, Compose fournit AnnotatedString, son propre équivalent de SpannableString avec des capacités similaires : SpanStyle pour les styles de caractères individuels et ParagraphStyle pour les paragraphes. La conversion de SpannableString en AnnotatedString est possible via buildAnnotatedString avec itération sur les spans.

Comment créer un Span personnalisé ?

Créez une classe qui étend CharacterStyle et redéfinissez la méthode updateDrawState(TextPaint tp). À l'intérieur de la méthode, modifiez les propriétés de TextPaint : couleur, épaisseur de trait, effets. Pour les changements métriques, utilisez UpdateLayout ou MetricAffectingSpan. Les spans personnalisés sont appliqués via setSpan() comme les spans intégrés.

Résumé

  • SpannableString — une classe Android pour styliser du texte via des objets Span avec liaison de plages de caractères
  • CharacterStyle affecte les caractères individuels, ParagraphStyle affecte les paragraphes entiers
  • Spans principaux : ForegroundColorSpan, StyleSpan, RelativeSizeSpan, URLSpan, ClickableSpan
  • SpannableStringBuilder — une version mutable pour la construction dynamique de texte avec Editable
  • Combiner des spans est sûr — chaque span modifie son propre attribut indépendamment
  • Les flags de Span contrôlent le comportement du span lors de l'insertion de texte aux limites
  • Optimisation : utilisez TextAppearanceSpan pour les groupes de styles et limitez les spans à 100

Nous développerons une application mobile clé en main

IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.

Discuter du projet

Lisez aussi