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