É possível ter vários estilos dentro de um TextView?


547

É possível definir vários estilos para diferentes partes do texto dentro de um TextView?

Por exemplo, estou definindo o texto da seguinte maneira:

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

É possível ter um estilo diferente para cada elemento do texto? Por exemplo, linha1 em negrito, palavra1 em itálico etc.

As tarefas comuns do guia do desenvolvedor e como executá-las no Android incluem partes de texto para seleção, destaque ou estilo :

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

Mas isso usa números de posição explícitos dentro do texto. Existe uma maneira mais limpa de fazer isso?


6
Se a string do TextView for estática, você pode adicionar as tags html <b>, <i> e <u> ao arquivo de recursos das strings e elas serão aplicadas automaticamente. Por exemplo, <TextView android: text = "@ string / test" /> em que @ string / test está definido como <string> <b> negrito </b>, <i>italic</i> </string>
— greg7gkb

2
+1 @ greg7gkb! A palavra-chave é 'estática'. Eu estava puxando meu cabelo, imaginando por que algumas das minhas cordas funcionavam com <b> e outras não. Os que não tinham variáveis ​​neles.
— 22613 Scott Scott (

Respostas:


694

Caso alguém esteja se perguntando como fazer isso, aqui está uma maneira: (Obrigado a Mark novamente!)

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

Para obter uma lista não oficial de tags suportadas por esse método, consulte este link ou esta pergunta: Quais tags HTML são suportadas pelo Android TextView?


1
@ JānisGruzis: Talvez uma maneira de fazer isso é através de um stub, por exemplo, formatTextWhite(string text)que só insere o texto na seguinte seqüência de formato: "<font size="..." color="..." face="...">%s</font>".
— Legend

@Legend eu usei <font fgcolor = '# ffff5400'> <b> <big> "+" Title "+" </big> </b> </font>, mas o fgcolor / color (ambos tentados) não é trabalhando ... você sabe como fazer a coisa cor usando html
— Muhammad Babar

@MuhammadBabar: Você pode tentar isso Html.fromHtml("<![CDATA[<font color='#ffff5400'>the html content you already have</font>]]>");:? Lembro que isso funcionou em algum momento para mim. Não tenho certeza se ainda funciona.
— Legend

1
@Legend obrigado, mas um tem uma maneira de contornar isso Html.fromHtml("<font color=\"#999999\">as sequências de escape trabalhou para mim:) ...
— Muhammad Babar

2
por que isso não funciona mBox..setText(Html.fromHtml("<b>" + name + "</b>") + doNotApplyHTML);alguma idéia graças
— Shan Xeeshi

216

Experimente Html.fromHtml()e marque seu texto com tags HTML em negrito e itálico, por exemplo:

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

1
Na verdade, isso me leva a outra pergunta: é melhor ter uma visualização de texto com texto html dentro dela ou três visualizações de texto com configurações de marcações diferentes sem usar a classe html? Estou assumindo a sua, obviamente, o primeiro, mas só queria confirmar ...
— Legend

2
Eu não tenho idéia do que a lista completa de tags que Html.fromHtml () suporta - você precisaria olhar o código-fonte. A marcação embutida e vários widgets TextView devem ser decisões ortogonais. Use vários widgets se precisar de posicionamento preciso de bits discretos de texto. Use a marcação embutida se você precisar de marcação embutida em um widget. Lembre-se: não há FlowLayout no Android, portanto, reunir vários TextViews para criar um parágrafo não é um AFAIK verdadeiramente prático.
— CommonsWare 7/10/09

1
Obrigado por isso ... Na verdade, a tag <small> funcionou ... Então, eu vou mantê-lo simples e apenas usá-lo ...
— Legend

1
@ TimBoland: Você está jogando fora seus vãos monthText + " " + month.getYearLabel(). Veja StringUtils.concat().
— CommonsWare

2
@ TimBoland: Você pode fazer isso tudo em um:, monthYearLabel.setText(Html.fromHtml("<b>"+month.getMonthLabel().toUpperCase()+"</b>&nbsp;"+month.getYearLabel()))se não precisar das peças individuais.
— CommonsWare

191

Um pouco fora do tópico, mas achei útil demais para não ser mencionado aqui.

E se gostaríamos de ler o texto Html do recurso string.xml e, assim, facilitar a localização. O CDATA torna isso possível:

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

A partir do nosso código Java, agora podemos utilizá-lo desta forma:

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

Eu não esperava que isso funcionasse. Mas sim.

Espero que seja útil para alguns de vocês!


2
Normalmente, "false" implica que você acessou um valor estranho na tabela de pesquisa. Eu tenho isso quando eu tinha R.id.ok e R.string.ok, e acidentalmente usado getString (R.id.ok) em vez do getString correta (R.string.ok)
— Joe Plante

Isso funciona em um layout XML também? Quando me refiro ao recurso de cadeia de caracteres com "@string/someText"(onde "someText" é um recurso definido em um arquivo strings.xml), recebo a cadeia de caracteres com todas as tags HTML como 'texto'.
— Gee.E

3
Achei essa a melhor resposta. Me inspirou. Não pude deixar de colocar os meus dois centavos no valor de resposta também. Solução aqui gist.github.com/aegis1980/b138dcb2fd1b2e98aa30 permite atribuir no arquivo de layout para que você não precisa de recursos de texto programaticamente atribuir (e pode visualizar no Android Studio!)
— Jon

127

Se você não quiser usar html, crie um styles.xml e use-o assim:

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

copie / cole-o em um aplicativo android simples em que você esteja trabalhando. Você verá que em uma única visualização de texto serão aplicados dois estilos.
— 217127 Kent Andersen

desculpe, eu não estava claro. se você precisar especificar a substring (por meio dos dois índices) de onde aplicar o estilo, isso não funcionará para cadeias localizadas porque os índices serão, obviamente, diferentes para cada localidade.
— 91112 Jeffrey Blattman

Você está certo. Se você suporta vários idiomas, esse não seria o caminho que você gostaria de seguir. A menos que você tenha certeza de que a sequência não mudará de tamanho ... como o nome do aplicativo, por exemplo.
— 227127

9
Não vejo por que l10n não pode trabalhar com esta solução. Apenas obtenha seqüências separadas em vez de apenas uma: String firstPart = getString (R.string.line1); String secondPart = getString (R.string.line2); Texto spannable = novo SpannableString (firstPart + secondPart); text.setSpan (novo ForegroundColorSpan (Color.parseColor (TEXT_COLOR_FOR_FIRST_PART)), 0, firstPart.length (), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); myTextView.setText (texto, TextView.BufferType.SPANNABLE);
— Rui

5
@Rui porque l10n também é posicional. você não pode codificar a posição das palavras em uma frase e ainda assim ser l10n. é por isso que você vê coisas como $1%s is a happy $2%sem pacotes de strings. você precisa permitir que os tokens sejam reorganizados.
— Jeffrey Blattman

60

É mais leve usar uma SpannableStringmarcação em vez de html. Isso me ajuda a ver exemplos visuais, então aqui está uma resposta suplementar.

insira a descrição da imagem aqui

Este é um 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());

Um estudo mais aprofundado

Este exemplo foi originalmente inspirado a partir daqui .


5
Sua postagem é melhor que a Documentação do Google sobre SpannableString .. Obrigado
— MBH

Isso deu suporte a imagens do serviço da web. Minha exigência assim.
— Mohanraj S

@MohanRajS SpannableStrings suporta emoji Unicode, mas não outras imagens.
— Suragch 8/02/19

@ Suragch obrigado pela pronta resposta, mas a minha exigência lateral é jpg, png. A única opção para lidar é com o webview? :(
— MohanRaj S

43

A lista de tags suportadas é:

Se você usar um recurso de cadeia, poderá adicionar um estilo simples, como negrito ou itálico, usando a notação HTML. As tags atualmente suportadas são:B (negrito), I(itálico), U(sublinhado), TT(monospace), BIG, SMALL, SUP(sobrescrito), SUB(índice), e STRIKE(tachado). Então, por exemplo, res/values/strings.xmlvocê pode declarar isso:

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

(Em http://developer.android.com/guide/faq/commontasks.html#selectingtext - link Arquivo da web, o <resource>erro de digitação está no original!)

Também mostra que Html.fromHtmlnão é realmente necessário em casos simples.


Html.fromHtml geralmente é uma maneira mais fácil de estilizar texto. Além disso, este link já está na pergunta original.
— Intrications

17

Eu estava com o mesmo problema. Eu poderia usar fromHtml, mas agora sou android, não web, então decidi tentar isso. Eu tenho que localizar isso, então eu dei uma chance usando o conceito de substituição de string. Defino o estilo no TextView como o estilo principal e, em seguida, apenas formato as outras partes.

Espero que isso ajude outras pessoas que desejam fazer a mesma coisa - não sei por que isso não é mais fácil na estrutura.

Minhas cordas ficam assim:


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

Aqui estão os estilos:


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

Aqui está o meu código que chama meu método 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);

O método de formato:


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

Eu não acho que isso funcionará se uma versão localizada da string reordenar {0} e {1}. Talvez você precise alterar startLocation0 se startLocation1 for <startLocation0
— JonathanC


8

Aqui está uma maneira fácil de fazer isso usando 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()
    );

Resultado:

Algum texto em negrito Algum texto em itálico


7

Se você quiser adicionar o texto estilizado em xml, poderá criar uma exibição personalizada estendendo o TextView e substituir 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);
    }
}

Em seguida, você pode usá-lo assim (substitua PACKAGE_NAMEpelo nome do seu pacote):

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

Será visualizado no Android Studio corretamente?
— JPM 26/06


3

Eu também

Que tal usar uma bela marcação com 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)
            }
        }
    }
}

Criado com marcação bonita


2

Sim, é possível usar SpannedString. Se você estiver usando o Kotlin, fica ainda mais fácil usar core-ktx, pois fornece uma linguagem específica de domínio (DSL) para fazer isso:

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

Mais opções fornecidas por ele são:

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

Por fim, você pode apenas:

textView.text = string

0

De fato, exceto o objeto Html, você também pode usar as classes de tipo Spannable, por exemplo, TextAppearanceSpan ou TypefaceSpan e SpannableString. A classe HTML também usa esses mecanismos. Mas com as classes do tipo Spannable, você tem mais liberdade.


0

Pode ser tão simples quanto aproveitar o método length () da String:

  1. Divida a string de texto no arquivo XML Strings em tantas sub-strings (uma string separada do ponto de vista do Android) quanto você precisar de estilos diferentes, para que possa ser como: str1, str2, str3 (como no seu caso), que, quando unidas, são toda a sequência que você usa.

  2. E, em seguida, basta seguir o método "Span", exatamente como você apresentou com seu código - mas, em vez de uma única string, combine todas as substrings que as fundem em uma única, cada uma com um estilo personalizado diferente.

Você ainda usa os números, mas não diretamente - eles não assumem uma forma codificada (como no seu código) agora, mas estão sendo substituídos pelos métodos length () combinados (observe duas estrelas precedendo e sufixando o str. length () no lugar do número absoluto para extinção da alteração):

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

para o primeiro tamanho de string, str.length () + 1, str.length () + str2.length () para o segundo tamanho de string, e assim por diante com todas as substrings, em vez de, por exemplo, 0,7 ou 8,19 e assim por diante...


0

O uso de uma classe Spannable auxiliar como o Android String Resources compartilha na parte inferior da página da web. Você pode abordar isso criando CharSquencese dando a eles um estilo.

Mas no exemplo que eles nos dão, é apenas para texto em negrito, itálico e até mesmo para colorir. Eu precisava agrupar vários estilos em um CharSequencepara defini-los em um TextView. Então, para essa classe (eu o nomeei CharSequenceStyles), acabei de adicionar essa função.

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

E na visão eu adicionei isso.

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

Espero que isso lhe ajude!


0

Spanny torna o SpannableString mais fácil de usar.

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

0

Como Jon disse , para mim esta é a melhor solução e você não precisa definir nenhum texto em tempo de execução, use apenas essa classe personalizada 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 é isso, agora só coloque no seu 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" />

com sua String Html

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

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.