È possibile avere più stili all'interno di un TextView?


547

È possibile impostare più stili per diversi pezzi di testo all'interno di TextView?

Ad esempio, sto impostando il testo come segue:

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

È possibile avere uno stile diverso per ciascun elemento di testo? Ad esempio, line1 grassetto, word1 corsivo, ecc.

Le attività comuni della guida per sviluppatori e come eseguirle in Android includono la selezione, l'evidenziazione o lo stile di parti di testo :

// 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);

Ma questo utilizza numeri di posizione espliciti all'interno del testo. C'è un modo più pulito per farlo?


6
Se la stringa di TextView è statica, puoi semplicemente aggiungere tag html <b>, <i> e <u> nel file di risorse delle stringhe e verranno automaticamente applicati. Ad esempio <TextView android: text = "@ string / test" /> dove @ string / test è impostato su <string> <b> grassetto </b>, <i>italic</i> </string>
greg7gkb

2
+1 @ greg7gkb! La parola chiave è "statico". Mi stavo togliendo i capelli chiedendomi perché alcune delle mie corde funzionassero con <b> e altre no. Quelli che non contenevano variabili.
Scott Biggs,

Risposte:


694

Nel caso in cui qualcuno si stia chiedendo come farlo, ecco un modo: (Grazie ancora a Mark!)

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

Per un elenco non ufficiale di tag supportati da questo metodo, fai riferimento a questo link o a questa domanda: quali tag HTML sono supportati da Android TextView?


1
@ JānisGruzis: Forse un modo per farlo è attraverso uno stub, per esempio, formatTextWhite(string text)che appena inserisce il testo nella seguente stringa di formato: "<font size="..." color="..." face="...">%s</font>".
Legenda

@Legend ho usato <font fgcolor = '# ffff5400'> <b> <big> "+" Title "+" </big> </b> </font> ma fgcolor / color (entrambi provati) non è funzionante ... sai come fare la cosa del colore usando html
Muhammad Babar

@MuhammadBabar: Si può provare questo: Html.fromHtml("<![CDATA[<font color='#ffff5400'>the html content you already have</font>]]>");? Ricordo che questo ha funzionato qualche volta per me. Non sono sicuro che funzioni ancora.
Legenda,

1
@Legend grazie ma ho trovato un modo per evitarlo, Html.fromHtml("<font color=\"#999999\">le sequenze di escape hanno funzionato per me :) ...
Muhammad Babar

2
perché questo non ha mBox..setText(Html.fromHtml("<b>" + name + "</b>") + doNotApplyHTML);alcuna idea grazie
Shan Xeeshi,

216

Prova Html.fromHtml()e contrassegna il testo con tag HTML in grassetto e corsivo, ad esempio:

Spanned text = Html.fromHtml("This mixes <b>bold</b> and <i>italic</i> stuff");
textView.setText(text);

1
In realtà questo mi porta a un'altra domanda: è meglio avere una visualizzazione di testo con testo html al suo interno o tre visualizzazioni di testo con impostazioni di markup diverse senza utilizzare la classe html? Presumo sia ovviamente il primo, ma volevo solo confermarlo ...
Leggenda

2
Non ho idea di quale sia la lista completa di tag supportata da Html.fromHtml () - dovresti guardare il codice sorgente. Il markup in linea e più widget TextView dovrebbero essere decisioni ortogonali. Utilizzare più widget se è necessario il posizionamento preciso di bit di testo discreti. Usa il markup inline se hai bisogno di markup inline in un widget. Ricorda: non esiste FlowLayout in Android, quindi mettere insieme più TextView per creare un paragrafo non è davvero pratico AFAIK.
CommonsWare,

1
Grazie per quello ... In realtà, il tag <small> ha funzionato ... Quindi lo terrò semplice e lo userò solo ...
Legenda

1
@TimBoland: stai gettando via le tue campate monthText + " " + month.getYearLabel(). Vedere StringUtils.concat().
CommonsWare,

2
@ Timimoland: potresti farlo tutto in uno: monthYearLabel.setText(Html.fromHtml("<b>"+month.getMonthLabel().toUpperCase()+"</b>&nbsp;"+month.getYearLabel()))se non hai bisogno dei singoli pezzi.
CommonsWare,

191

Leggermente fuori tema, ma l'ho trovato troppo utile per non essere menzionato qui.

E se volessimo leggere il testo HTML dalla risorsa string.xml e rendere così facile la localizzazione. CDATA lo rende possibile:

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

Dal nostro codice Java ora possiamo utilizzarlo in questo modo:

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

Non mi aspettavo che funzionasse. Ma lo ha fatto.

Spero sia utile per alcuni di voi!


2
Di solito "false" implica che è stato effettuato l'accesso a un valore strano nella tabella di ricerca. Ho avuto questo quando ho avuto R.id.ok e R.string.ok e ho usato accidentalmente getString (R.id.ok) invece del corretto getString (R.string.ok)
Joe Plante,

Funziona anche in un layout-XML? Quando mi riferisco alla risorsa stringa lì con "@string/someText"(dove "someText" è una risorsa definita in strings.xml), ottengo semplicemente la stringa con tutti i tag HTML come "testo".
Gee.E,

3
Ho trovato questa la risposta migliore. Mi ha ispirato. Non ho potuto fare a meno di dare anche la risposta ai miei due centesimi. La soluzione qui gist.github.com/aegis1980/b138dcb2fd1b2e98aa30 ti consente di assegnare un file di layout in modo da non dover assegnare risorse di testo a livello di codice (e puoi visualizzare l'anteprima in Android Studio!)
Jon

127

Se non hai voglia di usare HTML, puoi semplicemente creare un styles.xml e usarlo in questo modo:

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);

copiarlo / incollarlo in una semplice applicazione Android su cui potresti lavorare. Vedrai che su una singola visualizzazione di testo verranno applicati due stili.
Kent Andersen,

scusa non sono stato chiaro. se devi specificare la sottostringa (tramite i due indici) di dove applicare lo stile, ciò non funziona per le stringhe localizzate perché gli indici saranno ovviamente diversi per ogni locale.
Jeffrey Blattman,

Hai ragione. Se stai supportando più lingue, questa non sarebbe la strada che vorresti intraprendere. A meno che tu non fossi sicuro che la stringa non cambierebbe dimensioni ... come ad esempio il nome dell'applicazione.
Kent Andersen,

9
Non vedo perché l10n non possa funzionare con questa soluzione. Ottieni stringhe separate invece di una sola: String firstPart = getString (R.string.line1); String secondPart = getString (R.string.line2); Spannable text = new SpannableString (firstPart + secondPart); text.setSpan (nuovo ForegroundColorSpan (Color.parseColor (TEXT_COLOR_FOR_FIRST_PART)), 0, firstPart.length (), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); myTextView.setText (testo, TextView.BufferType.SPANNABLE);
Rui,

5
@Rui perché anche l10n è posizionale. non puoi codificare la posizione delle parole in una frase ed essere ancora l10n. ecco perché vedi cose come $1%s is a happy $2%snei bundle di stringhe. devi consentire la riorganizzazione dei token.
Jeffrey Blattman,

60

È più leggero usare un SpannableStringmarkup anziché HTML. Mi aiuta a vedere esempi visivi, quindi ecco una risposta supplementare.

inserisci qui la descrizione dell'immagine

Questo è un singolo 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());

Ulteriore studio

Questo esempio è stato originariamente ispirato da qui .


5
Il tuo post è migliore della documentazione di Google su SpannableString .. Grazie
MBH,

Questo ha supportato immagini che dal servizio web. Il mio requisito è così.
MohanRaj S

@MohanRajS SpannableStrings supporta le emoji Unicode, ma non altre immagini.
Suragch

@Suragch grazie per la pronta risposta, ma il mio requisito secondario è jpg, png. L'unica scelta da gestire è webview? :(
MohanRaj S,

43

L'elenco dei tag supportati è:

Se si utilizza una risorsa stringa, è possibile aggiungere alcuni stili semplici, come grassetto o corsivo utilizzando la notazione HTML. I tag attualmente supportati sono: B(in grassetto), I(corsivo), U(sottolineatura), TT(monospazio), BIG, SMALL, SUP(apice), SUB(pedice), e STRIKE(barrato). Quindi, ad esempio, res/values/strings.xmlpotresti dichiarare questo:

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

(Da http://developer.android.com/guide/faq/commontasks.html#selectingtext - collegamento Web Archive, l' <resource>errore di battitura è in originale!)

Mostra anche che Html.fromHtmlnon è davvero necessario in casi semplici.


Html.fromHtml è spesso un modo più semplice per dare uno stile al testo. Inoltre, questo link è già nella domanda originale.
Intricitazioni

17

Stavo riscontrando lo stesso problema. Potrei usare da HTML, ma ora sono Android, non Web, quindi ho deciso di provarlo. Devo localizzarlo però, quindi ho provato a usare il concetto di sostituzione delle stringhe. Ho impostato lo stile su TextView come principale, quindi ho semplicemente formattato gli altri peices.

Spero che questo aiuti gli altri a cercare di fare la stessa cosa - non so perché questo non sia più facile nel quadro.

Le mie corde sembrano così:


<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>

Ecco gli stili:


<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>

Ecco il mio codice che chiama il mio metodo 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);

Il metodo di formattazione:


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;
}

Non penso che funzionerà se una versione localizzata delle stringhe riordina {0} e {1}. Potrebbe essere necessario modificare startLocation0 se startLocation1 è <startLocation0
JonathanC


8

Ecco un modo semplice per farlo utilizzando 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()
    );

Risultato:

Alcuni caratteri in grassetto Alcuni testi in corsivo


7

Se vuoi essere in grado di aggiungere il testo con stile in xml puoi creare una vista personalizzata estendendo TextView e sovrascrivendo 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);
    }
}

Quindi, è possibile utilizzarlo in questo modo (sostituirlo PACKAGE_NAMEcon il nome del pacchetto):

<PACKAGE_NAME.HTMLStyledTextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="<![CDATA[
        <b>Bolded Text:</b> Non-Bolded Text
    ]]>"
/>

L'anteprima in Android Studio è corretta?
JPM,


3

Anch'io

Che ne dici di usare un bel markup con Kotlin e 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)
            }
        }
    }
}

Creato con Beautiful Markup


2

Sì, è possibile utilizzare SpannedString. Se stai usando Kotlin, diventa ancora più facile da fare usando core-ktx, in quanto fornisce un dominio specifico del linguaggio (DSL) per fare questo:

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

Altre opzioni fornite sono:

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

Finalmente puoi semplicemente:

textView.text = string

0

Infatti, ad eccezione dell'oggetto Html, è possibile utilizzare anche le classi di tipi Spannable, ad esempio TextAppearanceSpan o TypefaceSpan e SpannableString togather. Anche la classe HTML utilizza questi meccanismi. Ma con le classi di tipo Spannable, hai più libertà.


0

Potrebbe essere semplice come sfruttare il metodo length () di String:

  1. Dividi la stringa di testo nel file XML Stringhe in altrettante stringhe secondarie (stringhe separate dal punto di vista di Android) quante ne richiedono stili diversi, quindi potrebbe essere come: str1, str2, str3 (come nel tuo caso), che quando uniti insieme sono l'intera singola stringa che usi.

  2. Quindi segui semplicemente il metodo "Span", proprio come hai presentato con il tuo codice - ma invece di una singola stringa, combina tutte le sottostringhe unendole in una singola, ognuna con uno stile personalizzato diverso.

Usi ancora i numeri, anche se non direttamente: non hanno più una forma codificata (come nel tuo codice) ora, ma vengono sostituiti con i metodi combinati di lunghezza () (nota due stelle che precedono e suffissano la str. lunghezza () al posto del numero assoluto per estinguere la modifica):

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

per la prima dimensione della stringa, quindi str.length () + 1, str.length () + str2.length () per la seconda dimensione della stringa e così via con tutte le sottostringhe, anziché ad esempio 0,7 o 8,19 e così via...


0

Utilizzo di una classe Spannable ausiliaria come condivisioni Risorse stringa Android nella parte inferiore della pagina Web. Puoi affrontarlo creando CharSquencese dando loro uno stile.

Ma nell'esempio che ci danno, è solo per grassetto, corsivo e persino per colorare il testo. Avevo bisogno di avvolgere diversi stili in CharSequencea per metterli in a TextView. Quindi a quella classe (l'ho chiamata CharSequenceStyles) ho appena aggiunto questa funzione.

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

E nella vista l'ho aggiunto.

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

Spero che questo ti sia d'aiuto!


0

Spanny rende SpannableString più facile da usare.

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

0

Come ha detto Jon , per me questa è la soluzione migliore e non è necessario impostare alcun testo in fase di esecuzione, utilizzare solo questa classe personalizzata 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);
  }

}

e questo è tutto, ora inseriscilo solo nel tuo 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" />

con la tua stringa HTML

<string name="example_html">
<![CDATA[
<b>Author:</b> Mr Donuthead<br/>
<b>Contact:</b> me@donut.com<br/>
<i>Donuts for life </i>
]]>

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.