I am looking to change the text of a TextView view via the .setText("") method while also coloring a part of the text (or making it bold, italic, transparent, etc.)and not the rest. For example:

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

I know the above code is incorrect but it helps illustrate what I would like to achieve. How would I do this?

Solution 1

Use spans.

Example:

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

Solution 2

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

Solution 3

I hope this helps you (it works with multi language).

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

And on your java code, you can do:

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

This way, only the "Test" part will be colored (and bold).

Solution 4

If you are using Kotlin you can do the following using the android-ktx library

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

titleTextField.text = title

The bold is an extension function on SpannableStringBuilder. You can see the documentation here for a list of operations you can use.

Another example:

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

Where green is a resolved RGB color.

It is even possible to nest spans so you end up with something like an embedded DSL:

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

You will need the following in your app module level build.gradle for it to work:

repositories {
    google()
}

dependencies {
    implementation 'androidx.core:core-ktx:0.3'
}

Solution 5

Here's an example that will look for all occurrences of a word (case insensitive), and color them red:

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

Solution 6

You can use a Spannable to give certain parts of a text certain aspects. I can look up an example if you want.

Ah, from right here on 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);

Solution 7

If you want to use HTML, you need to use TextView.setText(Html.fromHtml(String htmlString))

If you want to do that often / repeatedly, you may have a look at a class (SpannableBuilder) I wrote, as Html.fromHtml() is not very efficient (it is using a big xml parsing machinery inside). It is described in this blog posting.

Solution 8

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

or use this code:

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

Solution 9

I like to use SpannableStringBuilder by appending the different spans one by one, rather than calling setSpan by calculating the string lengths

as: (Kotlin code)

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

Solution 10

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

Using

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

textView.setText(spannable);

Solution 11

You can concatenate two or more Spans. This way is easier to color dynamic text using length value.

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

Solution 12

Use this code its helpful

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

Solution 13

You can use extension function in Kotlin

fun CharSequence.colorizeText(
    textPartToColorize: CharSequence,
    @ColorInt color: Int
): CharSequence = SpannableString(this).apply {
    val startIndexOfText = this.indexOf(textPartToColorize.toString())
    setSpan(ForegroundColorSpan(color), startIndexOfText, startIndexOfText.plus(textPartToColorize.length), 0)
}

Usage:

val colorizedText = "this text will be colorized"
val myTextToColorize = "some text, $colorizedText continue normal text".colorizeText(colorizedText,ContextCompat.getColor(context, R.color.someColor))

Solution 14

Html.fromHtml is deprecated

Use HtmlCompat instead

HtmlCompat.fromHtml(html, HtmlCompat.FROM_HTML_MODE_LEGACY)

Solution 15

If you do not want to get in trouble on lower SDK version use SpannableStringBuilder with ForegroundColorSpan or BackgroundColorSpan as HtmlCompat.fromHtml color style does not applied on older Android version.