Est-il possible d'avoir plusieurs styles dans une TextView?

547

Est-il possible de définir plusieurs styles pour différents morceaux de texte dans une TextView?

Par exemple, je mets le texte comme suit:

tv.setText(line1 + "\n" + line2 + "\n" + word1 + "\t" + word2 + "\t" + word3);

Est-il possible d'avoir un style différent pour chaque élément de texte? Par exemple, ligne1 en gras, mot1 en italique, etc.

Les tâches courantes du guide du développeur et comment les faire dans Android comprennent la sélection, la mise en évidence ou le style de parties de texte :

// Get our EditText object.
EditText vw = (EditText)findViewById(R.id.text);

// Set the EditText's text.
vw.setText("Italic, highlighted, bold.");

// If this were just a TextView, we could do:
// vw.setText("Italic, highlighted, bold.", TextView.BufferType.SPANNABLE);
// to force it to use Spannable storage so styles can be attached.
// Or we could specify that in the XML.

// Get the EditText's internal text storage
Spannable str = vw.getText();

// Create our span sections, and assign a format to each.
str.setSpan(new StyleSpan(android.graphics.Typeface.ITALIC), 0, 7, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
str.setSpan(new BackgroundColorSpan(0xFFFFFF00), 8, 19, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
str.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), 21, str.length() - 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

Mais cela utilise des numéros de position explicites à l'intérieur du texte. Existe-t-il une façon plus propre de procéder?

Légende
la source
6
Si la chaîne TextView est statique, vous pouvez simplement ajouter des balises html <b>, <i> et <u> dans le fichier de ressources de chaînes et elles seront automatiquement appliquées. Par exemple, <TextView android: text = "@ string / test" /> où @ string / test est défini sur <string> <b> bold </b>, <i>italic</i> </string>
greg7gkb
2
+1 @ greg7gkb! Le mot clé est «statique». Je tirais mes cheveux en me demandant pourquoi certaines de mes cordes fonctionnaient avec <b> et d'autres non. Celles qui n'avaient pas de variables.
Scott Biggs

Réponses:

694

Au cas où quelqu'un se demanderait comment faire cela, voici une façon: (Merci encore à Mark!)

mBox = new TextView(context);
mBox.setText(Html.fromHtml("<b>" + title + "</b>" +  "<br />" + 
            "<small>" + description + "</small>" + "<br />" + 
            "<small>" + DateAdded + "</small>"));

Pour une liste non officielle des balises prises en charge par cette méthode, reportez-vous à ce lien ou à cette question: Quelles balises HTML sont prises en charge par Android TextView?

Légende
la source
1
@ JānisGruzis: Peut-être une façon de le faire est par une méthode de talon, par exemple, formatTextWhite(string text)qui vient d' insérer le texte dans la chaîne de format suivant: "<font size="..." color="..." face="...">%s</font>".
Legend
@Legend j'ai utilisé <font fgcolor = '# ffff5400'> <b> <big> "+" Title "+" </big> </b> </font> mais le fgcolor / color (tous deux essayés) n'est pas travail ... savez-vous comment faire la couleur en utilisant html
Muhammad Babar
@MuhammadBabar: Pouvez - vous essayer ceci: Html.fromHtml("<![CDATA[<font color='#ffff5400'>the html content you already have</font>]]>");? Je me souviens que cela a fonctionné pour moi. Je ne sais pas si cela fonctionne toujours.
Legend
1
@Legend merci mais j'ai réussi à contourner Html.fromHtml("<font color=\"#999999\">les séquences d'échappement qui ont fonctionné pour moi :) ...
Muhammad Babar
2
pourquoi cela ne fonctionne mBox..setText(Html.fromHtml("<b>" + name + "</b>") + doNotApplyHTML);aucune idée merci
Shan Xeeshi
216

Essayez de Html.fromHtml()marquer votre texte avec des balises HTML en gras et en italique, par exemple:

Spanned text = Html.fromHtml("This mixes <b>bold</b> and <i>italic</i> stuff");
textView.setText(text);
CommonsWare
la source
1
En fait, cela m'amène à une autre question: est-il préférable d'avoir une vue de texte avec du texte html à l'intérieur ou trois vues de texte avec des balises différentes configurées sans utiliser la classe html? Je suppose que c'est évidemment le premier mais je voulais juste le confirmer ...
Légende
2
Je n'ai aucune idée de la liste complète des balises prises en charge par Html.fromHtml () - vous devez consulter le code source. Le balisage en ligne et plusieurs widgets TextView doivent être des décisions orthogonales. Utilisez plusieurs widgets si vous avez besoin d'un placement précis de bits de texte discrets. Utilisez le balisage en ligne si, euh, vous avez besoin d'un balisage en ligne dans un widget. N'oubliez pas: il n'y a pas de FlowLayout dans Android, donc enchaîner plusieurs TextViews pour créer un paragraphe n'est pas vraiment pratique AFAIK.
CommonsWare
1
Merci pour cela ... En fait, la balise <small> a fonctionné ... Donc je vais rester simple et l'utiliser simplement ...
Legend
1
@TimBoland: Vous jetez vos travées via monthText + " " + month.getYearLabel(). Tu vois StringUtils.concat().
CommonsWare
2
@TimBoland: Vous pouvez le faire tout en un: monthYearLabel.setText(Html.fromHtml("<b>"+month.getMonthLabel().toUpperCase()+"</b>&nbsp;"+month.getYearLabel()))si vous n'avez pas besoin des pièces individuelles.
CommonsWare
191

Légèrement hors sujet, mais j'ai trouvé cela trop utile pour ne pas être mentionné ici.

Et si nous voulions lire le texte Html de la ressource string.xml et ainsi le rendre facile à localiser. CDATA rend cela possible:

<string name="my_text">
  <![CDATA[
    <b>Autor:</b> Mr Nice Guy<br/>
    <b>Contact:</b> [email protected]<br/>
    <i>Copyright © 2011-2012 Intergalactic Spacebar Confederation </i>
  ]]>
</string> 

À partir de notre code Java, nous pourrions maintenant l'utiliser comme ceci:

TextView tv = (TextView) findViewById(R.id.myTextView);
tv.setText(Html.fromHtml(getString(R.string.my_text))); 

Je ne m'attendais pas à ce que cela fonctionne. Mais ça l'a fait.

J'espère que c'est utile pour certains d'entre vous!

Ben
la source
2
Habituellement, "false" implique que vous avez accédé à une valeur étrange sur la table de recherche. Je l'ai eu quand j'avais R.id.ok et R.string.ok, et j'ai accidentellement utilisé getString (R.id.ok) au lieu de la bonne getString (R.string.ok)
Joe Plante
Est-ce que cela fonctionne également dans une mise en page XML? Quand je me réfère à la ressource de chaîne là-bas avec "@string/someText"(où "someText" est une ressource définie dans un strings.xml), je reçois simplement la chaîne avec toutes les balises HTML en tant que "texte".
Gee.E
3
J'ai trouvé que c'était la meilleure réponse. M'a inspiré. Je n'ai pas pu m'empêcher de mettre ma réponse de deux cents aussi. La solution ici gist.github.com/aegis1980/b138dcb2fd1b2e98aa30 vous permet d'affecter dans un fichier de mise en page afin que vous n'ayez pas besoin d'affecter des ressources de texte par programme (et pouvez prévisualiser dans Android Studio!)
Jon
127

Si vous n'avez pas envie d'utiliser du HTML, vous pouvez simplement créer un styles.xml et l'utiliser comme ceci:

TextView tv = (TextView) findViewById(R.id.textview);
SpannableString text = new SpannableString(myString);

text.setSpan(new TextAppearanceSpan(getContext(), R.style.myStyle), 0, 5, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
text.setSpan(new TextAppearanceSpan(getContext(), R.style.myNextStyle), 6, 10, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

tv.setText(text, TextView.BufferType.SPANNABLE);
Kent Andersen
la source
copiez / collez-le dans une application Android simple sur laquelle vous travaillez. Vous verrez que sur une seule vue de texte, deux styles seront appliqués.
Kent Andersen
désolé je n'étais pas clair. si vous devez spécifier la sous-chaîne (via les deux index) où appliquer le style, cela ne fonctionne pas pour les chaînes localisées car les index seront bien sûr différents pour chaque locale.
Jeffrey Blattman
Vous avez raison. Si vous prenez en charge plusieurs langues, ce ne serait pas la route que vous souhaitez emprunter. Sauf si vous étiez sûr que la chaîne ne changerait pas de taille ... comme le nom de l'application par exemple.
Kent Andersen
9
Je ne vois pas pourquoi l10n ne peut pas fonctionner avec cette solution. Obtenez simplement des chaînes séparées au lieu d'une seule: String firstPart = getString (R.string.line1); String secondPart = getString (R.string.line2); Texte extensible = nouveau SpannableString (firstPart + secondPart); text.setSpan (new ForegroundColorSpan (Color.parseColor (TEXT_COLOR_FOR_FIRST_PART)), 0, firstPart.length (), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); myTextView.setText (texte, TextView.BufferType.SPANNABLE);
Rui
5
@Rui parce que l10n est également positionnel. vous ne pouvez pas coder en dur la position des mots dans une phrase tout en étant l10n. c'est pourquoi vous voyez des choses comme $1%s is a happy $2%sdans les faisceaux de chaînes. vous devez autoriser la réorganisation des jetons.
Jeffrey Blattman
60

Il est plus léger d'utiliser un SpannableStringbalisage au lieu de HTML. Cela m'aide à voir des exemples visuels alors voici une réponse supplémentaire.

entrez la description de l'image ici

C'est un single TextView.

// set the text
SpannableString s1 = new SpannableString("bold\n");
SpannableString s2 = new SpannableString("italic\n");
SpannableString s3 = new SpannableString("foreground color\n");
SpannableString s4 = new SpannableString("background color\n");
SpannableString s5 = new SpannableString("underline\n");
SpannableString s6 = new SpannableString("strikethrough\n");
SpannableString s7 = new SpannableString("bigger\n");
SpannableString s8 = new SpannableString("smaller\n");
SpannableString s9 = new SpannableString("font\n");
SpannableString s10 = new SpannableString("URL span\n");
SpannableString s11 = new SpannableString("clickable span\n");
SpannableString s12 = new SpannableString("overlapping spans\n");

// set the style
int flag = Spanned.SPAN_EXCLUSIVE_EXCLUSIVE;
s1.setSpan(new StyleSpan(Typeface.BOLD), 0, s1.length(), flag);
s2.setSpan(new StyleSpan(Typeface.ITALIC), 0, s2.length(), flag);
s3.setSpan(new ForegroundColorSpan(Color.RED), 0, s3.length(), flag);
s4.setSpan(new BackgroundColorSpan(Color.YELLOW), 0, s4.length(), flag);
s5.setSpan(new UnderlineSpan(), 0, s5.length(), flag);
s6.setSpan(new StrikethroughSpan(), 0, s6.length(), flag);
s7.setSpan(new RelativeSizeSpan(2), 0, s7.length(), flag);
s8.setSpan(new RelativeSizeSpan(0.5f), 0, s8.length(), flag);
s9.setSpan(new TypefaceSpan("monospace"), 0, s9.length(), flag);
s10.setSpan(new URLSpan("https://developer.android.com"), 0, s10.length(), flag);
s11.setSpan(new ClickableSpan() {
    @Override
    public void onClick(View widget) {
        Toast.makeText(getApplicationContext(), "Span clicked", Toast.LENGTH_SHORT).show();
    }
}, 0, s11.length(), flag);
s12.setSpan(new ForegroundColorSpan(Color.RED), 0, 11, flag);
s12.setSpan(new BackgroundColorSpan(Color.YELLOW), 4, s12.length(), flag);
s12.setSpan(new UnderlineSpan(), 4, 11, flag);

// build the string
SpannableStringBuilder builder = new SpannableStringBuilder();
builder.append(s1);
builder.append(s2);
builder.append(s3);
builder.append(s4);
builder.append(s5);
builder.append(s6);
builder.append(s7);
builder.append(s8);
builder.append(s9);
builder.append(s10);
builder.append(s11);
builder.append(s12);

// set the text view with the styled text
textView.setText(builder);
// enables clicking on spans for clickable span and url span
textView.setMovementMethod(LinkMovementMethod.getInstance());

Une étude plus approfondie

Cet exemple a été inspiré à l'origine d' ici .

Suragch
la source
5
Votre message est meilleur que la documentation de Google sur SpannableString .. Merci
MBH
Cela a-t-il pris en charge les images provenant du service Web? Mon exigence comme ça.
MohanRaj S
@MohanRajS SpannableStrings prend en charge les emoji Unicode, mais pas les autres images.
Suragch
@Suragch merci pour une réponse rapide, mais mon exigence latérale est jpg, png. Le seul choix à gérer est la visualisation Web? :(
MohanRaj S
43

La liste des balises prises en charge est:

Si vous utilisez une ressource de chaîne, vous pouvez ajouter un style simple, comme gras ou italique en utilisant la notation HTML. Les balises sont actuellement pris en charge: B(gras), I( en italique), U(souligné), TT(monospaces), BIG, SMALL, SUP(superscript), SUB(indice), et STRIKE(biffés). Ainsi, par exemple, res/values/strings.xmlvous pouvez déclarer ceci:

<resource>
    <string id="@+id/styled_welcome_message">We are <b><i>so</i></b> glad to see you.</string>
</resources>

(De http://developer.android.com/guide/faq/commontasks.html#selectingtext - lien Web Archive, la <resource>faute de frappe est dans l'original!)

Cela montre également que ce Html.fromHtmln'est pas vraiment nécessaire dans les cas simples.

Jon
la source
Html.fromHtml est souvent un moyen plus simple de styliser du texte. En outre, ce lien est déjà dans la question d'origine.
Intrications
17

Je rencontrais le même problème. Je pouvais utiliser fromHtml, mais je suis maintenant Android, pas web, j'ai donc décidé d'essayer cela. Je dois localiser cela, alors je l'ai essayé en utilisant le concept de remplacement de chaîne. J'ai défini le style sur TextView comme étant le style principal, puis je formate simplement les autres morceaux.

J'espère que cela aide les autres qui cherchent à faire la même chose - je ne sais pas pourquoi ce n'est pas plus facile dans le cadre.

Mes cordes ressemblent à ceci:


<string name="my_text">{0} You will need a {1} to complete this assembly</string>
<string name="text_sub0">1:</string>
<string name="text_sub1">screwdriver, hammer, and measuring tape</string>

Voici les styles:


<style name="MainStyle">
    <item name="android:textSize">@dimen/regular_text</item>
    <item name="android:textColor">@color/regular_text</item>
</style>
<style name="style0">
    <item name="android:textSize">@dimen/paragraph_bullet</item>
    <item name="android:textColor">@color/standout_text</item>
    <item name="android:textStyle">bold</item>
</style>
<style name="style1">
    <item name="android:textColor">@color/standout_light_text</item>
    <item name="android:textStyle">italic</item>
</style>

Voici mon code qui appelle ma méthode formatStyles:


SpannableString formattedSpan = formatStyles(getString(R.string.my_text), getString(R.string.text_sub0), R.style.style0, getString(R.string.main_text_sub1), R.style.style1);
textView.setText(formattedSpan, TextView.BufferType.SPANNABLE);

La méthode de formatage:


private SpannableString formatStyles(String value, String sub0, int style0, String sub1, int style1)
{
    String tag0 = "{0}";
    int startLocation0 = value.indexOf(tag0);
    value = value.replace(tag0, sub0);

    String tag1 = "{1}";
    int startLocation1 = value.indexOf(tag1);
    if (sub1 != null && !sub1.equals(""))
    {
        value = value.replace(tag1, sub1);
    }

    SpannableString styledText = new SpannableString(value);
    styledText.setSpan(new TextAppearanceSpan(getActivity(), style0), startLocation0, startLocation0 + sub0.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    if (sub1 != null && !sub1.equals(""))
    {
        styledText.setSpan(new TextAppearanceSpan(getActivity(), style1), startLocation1, startLocation1 + sub1.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    }

    return styledText;
}
farcrats
la source
Je ne pense pas que cela fonctionnera si une version localisée des chaînes réordonne {0} et {1}. Vous devrez peut-être modifier startLocation0 si startLocation1 est <startLocation0
JonathanC
14

Maintenant, l' <b>élément est obsolète. <strong>s'affiche en tant que <b>et <em>s'affiche en tant que <i>.

tv.setText(Html.fromHtml("<strong>bold</strong> and <em>italic</em> "));

cela fonctionne bien pour moi

Sandeep P
la source
8

Voici un moyen simple de le faire en utilisant HTMLBuilder

    myTextView.setText(new HtmlBuilder().
                    open(HtmlBuilder.Type.BOLD).
                    append("Some bold text ").
                    close(HtmlBuilder.Type.BOLD).
                    open(HtmlBuilder.Type.ITALIC).
                    append("Some italic text").
                    close(HtmlBuilder.Type.ITALIC).
                    build()
    );

Résultat:

Du texte en gras Du texte en italique

Ilya Gazman
la source
7

Si vous souhaitez pouvoir ajouter le texte stylisé en xml, vous pouvez créer une vue personnalisée étendant TextView et remplacer setText ():

public class HTMLStyledTextView extends TextView
{
    public HTMLStyledTextView(Context context) {
        super(context);
    }

    public HTMLStyledTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public HTMLStyledTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public void setText(CharSequence text, BufferType type)
    {
       super.setText(Html.fromHtml(text.toString()), type);
    }
}

Ensuite, vous pouvez l'utiliser comme ceci (remplacez-le PACKAGE_NAMEpar le nom de votre package):

<PACKAGE_NAME.HTMLStyledTextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="<![CDATA[
        <b>Bolded Text:</b> Non-Bolded Text
    ]]>"
/>
bcorso
la source
Est-ce qu'il prévisualise correctement dans Android Studio?
JPM
3

Moi aussi

Que diriez-vous d'utiliser un beau balisage avec Kotlin et Anko -

import org.jetbrains.anko.*
override fun onCreate(savedInstanceState: Bundle?) {
    title = "Created with Beautiful Markup"
    super.onCreate(savedInstanceState)

    verticalLayout {
        editText {
            hint = buildSpanned {
                append("Italic, ", Italic)
                append("highlighted", backgroundColor(0xFFFFFF00.toInt()))
                append(", Bold", Bold)
            }
        }
    }
}

Créé avec Beautiful Markup

Himanshu
la source
2

Oui, il est possible d'utiliser SpannedString. Si vous utilisez Kotlin, cela devient encore plus facile à faire en utilisant core-ktx, car il fournit un langage spécifique au domaine (DSL) pour ce faire:

    val string: SpannedString = buildSpannedString {
        bold {
            append("1111")
        }
        append("Devansh")     
    }

Plus d'options fournies par lui sont:

append("Hello There")
bold {
    append("bold")
    italic {
        append("bold and italic")
        underline {
            append("then some text with underline")
        }
    }
}

Enfin, vous pouvez simplement:

textView.text = string
Devansh Maurya
la source
0

En fait, à l'exception de l'objet Html, vous pouvez également utiliser les classes de type Spannable, par exemple TextAppearanceSpan ou TypefaceSpan et SpannableString togather. La classe HTML utilise également ces mécanismes. Mais avec les classes de type Spannable, vous avez plus de liberté.

Horloge ZHONG
la source
0

Cela peut être aussi simple que d'utiliser la méthode length () de String:

  1. Divisez la chaîne de texte du fichier XML Strings en autant de sous-chaînes (des chaînes distinctes du point de vue d'Android) que vous avez besoin de styles différents, de sorte que cela pourrait être comme: str1, str2, str3 (comme dans votre cas), qui, lorsqu'ils sont réunis, constituent la totalité de la chaîne unique que vous utilisez.

  2. Et puis suivez simplement la méthode "Span", comme vous l'avez présenté avec votre code - mais au lieu d'une seule chaîne, combinez toutes les sous-chaînes en les fusionnant en une seule, chacune avec un style personnalisé différent.

Vous utilisez toujours les chiffres, mais pas directement - ils ne prennent plus de forme codée en dur (comme dans votre code) maintenant, mais ils sont remplacés par les méthodes combinées length () (notez deux étoiles précédant et suffixant la chaîne. longueur () à la place du nombre absolu pour prolonger le changement):

str.setSpan(new StyleSpan(android.graphics.Typeface.ITALIC), 0, **str.length()**, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

pour la première taille de chaîne, puis str.length () + 1, str.length () + str2.length () pour la deuxième taille de chaîne, et ainsi de suite avec toutes les sous-chaînes, au lieu de 0,7 ou 8,19 etc...

forsberg
la source
0

Utilisation d'une classe auxiliaire pouvant être étendue en tant que partage de ressources de chaînes Android au bas de la page Web. Vous pouvez aborder cela en créant CharSquenceset en leur donnant un style.

Mais dans l'exemple qu'ils nous donnent, c'est juste pour le texte en gras, en italique et même en couleur. Je devais envelopper plusieurs styles dans un CharSequenceafin de les définir dans un TextView. Donc, à cette classe (je l'ai nommée CharSequenceStyles), je viens d'ajouter cette fonction.

public static CharSequence applyGroup(LinkedList<CharSequence> content){
    SpannableStringBuilder text = new SpannableStringBuilder();
    for (CharSequence item : content) {
        text.append(item);
    }
    return text;
}

Et dans la vue, j'ai ajouté cela.

            message.push(postMessageText);
            message.push(limitDebtAmount);
            message.push(pretMessageText);
            TextView.setText(CharSequenceStyles.applyGroup(message));

J'espère que cela vous aidera!

MontDeska
la source
0

Spanny facilite l'utilisation de SpannableString.

Spanny spanny = new Spanny("Underline text", new UnderlineSpan())
                .append("\nRed text", new ForegroundColorSpan(Color.RED))
                .append("\nPlain text");
textView.setText(spanny)
Desmond Lua
la source
0

Comme Jon l'a dit , pour moi, c'est la meilleure solution et vous n'avez pas besoin de définir de texte à l'exécution, utilisez uniquement cette classe personnalisée HtmlTextView

public class HtmlTextView extends TextView {

  public HtmlTextView(Context context) {
      super(context);
  }

  public HtmlTextView(Context context, AttributeSet attrs) {
      super(context, attrs);
  }

  public HtmlTextView(Context context, AttributeSet attrs, int defStyleAttr) 
  {
      super(context, attrs, defStyleAttr);
  }

  @TargetApi(21)
  public HtmlTextView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
      super(context, attrs, defStyleAttr, defStyleRes);
  }

  @Override
  public void setText(CharSequence s,BufferType b){
      super.setText(Html.fromHtml(s.toString()),b);
  }

}

et c'est tout, maintenant seulement le mettre dans votre XML

<com.fitc.views.HtmlTextView
    android:id="@+id/html_TV"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/example_html" />

avec votre chaîne Html

<string name="example_html">
<![CDATA[
<b>Author:</b> Mr Donuthead<br/>
<b>Contact:</b> [email protected]<br/>
<i>Donuts for life </i>
]]>

AWolfsdorf
la source