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

lenda
fonte
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?

lenda
fonte
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);
CommonsWare
fonte
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> [email protected]<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!

Ben
fonte
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);
Kent Andersen
fonte
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 .

Suragch
fonte
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.

Jon
fonte
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;
}
farcrats
fonte
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
14

Agora o <b>elemento está obsoleto. <strong>renderiza como <b>e <em>renderiza como <i>.

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

Este trabalho é bom para mim

Sandeep P
fonte
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

Ilya Gazman
fonte
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
    ]]>"
/>
bcorso
fonte
Será visualizado no Android Studio corretamente?
JPM 26/06
7

Conforme declarado, use TextView.setText(Html.fromHtml(String))

E use essas tags na sua string formatada em HTML:

<a href="...">
<b>
<big>
<blockquote>
<br>
<cite>
<dfn>
<div align="...">
<em>
<font size="..." color="..." face="...">
<h1>
<h2>
<h3>
<h4>
<h5>
<h6>
<i>
<img src="...">
<p>
<small>
<strike>
<strong>
<sub>
<sup>
<tt>
<u>

http://commonsware.com/blog/Android/2010/05/26/html-tags-supported-by-textview.html

Andrew Gallasch
fonte
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

Himanshu
fonte
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
Devansh Maurya
fonte
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.

Relógio ZHONG
fonte
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...

forsberg
fonte
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!

MontDeska
fonte
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)
Desmond Lua
fonte
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> [email protected]<br/>
<i>Donuts for life </i>
]]>

AWolfsdorf
fonte