Android: ¿Colorear parte de una cadena usando TextView.setText ()?

86

Estoy buscando cambiar el texto de una vista TextView a través del método .setText ("") mientras también coloreo una parte del texto (o lo hago en negrita, cursiva, transparente, etc.) y no el resto. Por ejemplo:

title.setText("Your big island <b>ADVENTURE!</b>";

Sé que el código anterior es incorrecto, pero ayuda a ilustrar lo que me gustaría lograr. ¿Cómo haría esto?

Jared
fuente
Si hay un texto largo en TextView, hay una forma más eficiente
Mingfei
Posible duplicado de Establecer color de TextView span en Android
Suragch
Solución en Kotlin usando Spans stackoverflow.com/a/59510004/11166067
Dmitrii Leonov

Respuestas:

202

Utilice tramos .

Ejemplo:

final SpannableStringBuilder sb = new SpannableStringBuilder("your text here");

// Span to set text color to some RGB value
final ForegroundColorSpan fcs = new ForegroundColorSpan(Color.rgb(158, 158, 158)); 

// Span to make text bold
final StyleSpan bss = new StyleSpan(android.graphics.Typeface.BOLD); 

// Set the text color for first 4 characters
sb.setSpan(fcs, 0, 4, Spannable.SPAN_INCLUSIVE_INCLUSIVE); 

// make them also bold
sb.setSpan(bss, 0, 4, Spannable.SPAN_INCLUSIVE_INCLUSIVE); 

yourTextView.setText(sb);
Alex Orlov
fuente
11
Realmente no me gusta la solución que ofrece Android aquí, ya que no funciona con varios idiomas, ya que no tengo idea de cuántos caracteres hay en mis palabras. Actualmente estoy buscando una solución en la que pueda tener varios tramos que pueda adjuntar todos a un TextView y luego se junten
philipp
1
El comentario que dice span para poner el texto en negrita, ¿debería decir span para hacer el color del texto?
Ryhan
9
@philipp si su problema es la cantidad de caracteres en su palabra, use el método length () en su String o StringBuilder o lo que sea
Ahmed Adel Ismail
1
Si hay un texto largo en TextView, aquí hay una forma más eficiente
Mingfei
Inicialmente me perdí esto cuando leí la respuesta, pero su índice final tiene que ser +1 el índice final en la cadena (es decir, para abarcar las primeras cuatro letras, debe establecer [inicio, fin] como [0, 4] no como [0, 3])
tir38
46
title.setText(Html.fromHtml("Your big island <b>ADVENTURE!</b>")); 
se sentó
fuente
2
Esta es una solución mucho mejor al problema original que la respuesta marcada.
BSnapZ
1
Si tiene saltos de línea \ n, esto no muestra los saltos de línea.
live-love
8
No, no es mejor, no hay una función para colorear, mencionada en la pregunta, pero el método SLOW fromHTML ()
Viktor Yakunin
1
¿Entonces el uso <font color="#FFAADD>text</font>no funcionará?
milosmns
25

Espero que esto te ayude (funciona con varios idiomas).

<string name="test_string" ><![CDATA[<font color="%1$s"><b>Test/b></font>]]> String</string>

Y en su código Java, puede hacer:

int color = context.getResources().getColor(android.R.color.holo_blue_light);
String string = context.getString(R.string.test_string, color);
textView.setText(Html.fromHtml(string));

De esta manera, solo la parte "Prueba" se coloreará (y se resaltará).

Luis
fuente
3
La mejor respuesta. Esto funciona perfectamente para cadenas internacionalizadas. También tiene la ventaja de poder tener su texto de color en el medio de su recurso de cadena.
jt-gilkeson
Para más explicaciones ver el Styling con HTML marcado sección aquí .
Elisabeth
17

Aquí hay un ejemplo que buscará todas las apariciones de una palabra (no distingue entre mayúsculas y minúsculas) y las coloreará de rojo:

String notes = "aaa AAA xAaax abc aaA xxx";
SpannableStringBuilder sb = new SpannableStringBuilder(notes);
Pattern p = Pattern.compile("aaa", Pattern.CASE_INSENSITIVE);
Matcher m = p.matcher(notes);
while (m.find()){
    //String word = m.group();
    //String word1 = notes.substring(m.start(), m.end());

    sb.setSpan(new ForegroundColorSpan(Color.rgb(255, 0, 0)), m.start(), m.end(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
}
editText.setText(sb);
vive el amor
fuente
me encanta tu nombre: P ¿Puedo cambiar el fondo de una palabra específica con un nuevo BackgroundColorSpan (Color.parseColor ("# BFFFC6"))?
Ajay Pandya
8

Puede usar a Spannablepara dar ciertos aspectos a ciertas partes de un texto. Puedo buscar un ejemplo si lo desea.

Ah, desde aquí en stackoverflow .

TextView TV = (TextView)findViewById(R.id.mytextview01); 
Spannable WordtoSpan = new SpannableString("I know just how to whisper, And I know just how to cry,I know just where to find the answers");        
WordtoSpan.setSpan(new ForegroundColorSpan(Color.BLUE), 15, 30, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
TV.setText(WordtoSpan);
Nanne
fuente
¿Cómo podemos configurar el rango inicial y final para texto en varios idiomas?
Mubarak
Una idea podría ser identificar la palabra que necesita resaltar y extraerla como un recurso de cadena independiente. Cuando traduzca la frase, deberá considerar actualizar la palabra separada también para que cuando configure el intervalo dependa del índice de la cadena y la longitud de la cadena para establecer dinámicamente los índices de inicio y finalización del intervalo.
Ionut Negru hace
8

Si está usando Kotlin, puede hacer lo siguiente usando la biblioteca android-ktx

val title = SpannableStringBuilder()
        .append("Your big island ")
        .bold { append("ADVENTURE") } 

titleTextField.text = title

El boldes una función de extensión en SpannableStringBuilder. Puede ver la documentación aquí para obtener una lista de las operaciones que puede utilizar.

Otro ejemplo:

val ssb = SpannableStringBuilder()
            .color(green) { append("Green text ") }
            .append("Normal text ")
            .scale(0.5F) { append("Text at half size ") }
            .backgroundColor(green) { append("Background green") }

Donde greenhay un color RGB resuelto.

Incluso es posible anidar intervalos para que termine con algo como un DSL integrado:

bold { underline { italic { append("Bold and underlined") } } }

Necesitará lo siguiente en el nivel de módulo de su aplicación build.gradlepara que funcione:

repositories {
    google()
}

dependencies {
    implementation 'androidx.core:core-ktx:0.3'
}
David Rawson
fuente
Además, no importa de qué color lo ponga, muestra el mismo color púrpura. val spannableStringBuilder = SpannableStringBuilder() spannableStringBuilder.bold { underline { color(R.color.test_color) { append(underlinedText) } } }
Abhishek Saxena
4

Si desea utilizar HTML, debe utilizar TextView.setText(Html.fromHtml(String htmlString))

Si desea hacer eso a menudo / repetidamente, puede echar un vistazo a una clase ( SpannableBuilder ) que escribí, ya Html.fromHtml()que no es muy eficiente (está usando una gran maquinaria de análisis xml en su interior). Se describe en esta publicación de blog .

Heiko Rupp
fuente
4
public static void setColorForPath(Spannable spannable, String[] paths, int color) {
    for (int i = 0; i < paths.length; i++) {
        int indexOfPath = spannable.toString().indexOf(paths[i]);
        if (indexOfPath == -1) {
            continue;
        }
        spannable.setSpan(new ForegroundColorSpan(color), indexOfPath,
                indexOfPath + paths[i].length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    }
}

Utilizando

Spannable spannable = new SpannableString("Your big island ADVENTURE");
Utils.setColorForPath(spannable, new String[] { "big", "ADVENTURE" }, Color.BLUE);

textView.setText(spannable);

ingrese la descripción de la imagen aquí

Phan Van Linh
fuente
3

            String str1 = "If I forget my promise to ";
            String penalty = "Eat breakfast every morning,";
            String str2 = " then I ";
            String promise = "lose my favorite toy";
           

            String strb = "<u><b><font color='#081137'>"+ penalty +",</font></b></u>";
            String strc = "<u><b><font color='#081137'>"+ promise + "</font></b></u>";
            String strd = str1 +strb+ str2 + strc;
           tv_notification.setText(Html.fromHtml(strd));

o usa este código:

    SpannableStringBuilder builder = new SpannableStringBuilder();
            SpannableString text1 = new SpannableString(str1);
            text1.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.silver)), 0, str1.length() - 1, 0);
            builder.append(text1);

            SpannableString text2 = new SpannableString(penalty);
            text2.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.midnight)), 0, penalty.length(), 0);
            text2.setSpan(new UnderlineSpan(), 0, penalty.length(), 0);
            builder.append(text2);

            SpannableString text3 = new SpannableString(str2);
            text3.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.silver)),0, str2.length(), 0);
            builder.append(text3);


            SpannableString text4 = new SpannableString(promise);
            text4.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.midnight)), 0, promise.length(), 0);
            text4.setSpan(new UnderlineSpan(),0, promise.length(), 0);
            builder.append(text4);

          tv_notification.setText(builder);

Nur Gazi
fuente
3

Me gusta usar SpannableStringBuilderagregando los diferentes intervalos uno por uno, en lugar de llamar a setSpan calculando las longitudes de las cadenas

como: (código Kotlin)

val amountSpannableString = SpannableString("₹$amount").apply {
  // text color
  setSpan(ForegroundColorSpan("#FD0025".parseColor()), 0, length, 0)
  // text size
  setSpan(AbsoluteSizeSpan(AMOUNT_SIZE_IN_SP.spToPx(context)), 0, length, 0)
  // font medium
  setSpan(TypefaceSpan(context.getString(R.string.font_roboto_medium)), 0, length, 0)
}

val spannable: Spannable = SpannableStringBuilder().apply {
  // append the different spans one by one
  // rather than calling setSpan by calculating the string lengths
  append(TEXT_BEFORE_AMOUNT)
  append(amountSpannableString)
  append(TEXT_AFTER_AMOUNT)
}

Resultado

vedant
fuente
2

Puede concatenar dos o más intervalos. De esta manera es más fácil colorear el texto dinámico usando el valor de longitud.

SpannableStringBuilder span1 = new SpannableStringBuilder("Android");
ForegroundColorSpan color1=new ForegroundColorSpan(getResources().getColor(R.color.colorPrimary));
span1.setSpan(color1, 0, span1.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);

SpannableStringBuilder span2 = new SpannableStringBuilder("Love");
ForegroundColorSpan color2=new ForegroundColorSpan(getResources().getColor(R.color.colorSecondary));
span2.setSpan(color2, 0, span2.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);

Spanned concatenated=(Spanned) TextUtils.concat(span1," => ",span2);

SpannableStringBuilder result = new SpannableStringBuilder(concatenated);

TextView tv = (TextView) rootView.findViewById(R.id.my_texview);
tv.setText(result, TextView.BufferType.SPANNABLE);
Jorge Palacio
fuente
2

Usa este código es útil

TextView txtTest = (TextView) findViewById(R.id.txtTest);
txtTest.setText(Html.fromHtml("This is <font color="#ff4343">Red</font> Color!"));
Nota de Hadi
fuente
0

Html.fromHtmlestá en desuso

Utilice HtmlCompat en su lugar

HtmlCompat.fromHtml(html, HtmlCompat.FROM_HTML_MODE_LEGACY)
Gastón Saillén
fuente