SpannableStringBuilder to create String with multiple fonts/text sizes etc Example?

90,442

Solution 1

First Part Not Bold   BOLD  rest not bold

You can do this either as @Rajesh suggested or by this.

String normalBefore= "First Part Not Bold ";
String normalBOLD=  "BOLD ";
String normalAfter= "rest not bold";
String finalString= normalBefore+normalBOLD+normalAfter;
Spannable sb = new SpannableString( finalString );
sb.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), finalString.indexOf(normalBOLD)+ normalBOLD.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); //bold
sb.setSpan(new AbsoluteSizeSpan(intSize), finalString.indexOf(normalBOLD)+ normalBOLD.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);//resize size

to show this in TextView

textview.setText(sb,  TextView.BufferType.SPANNABLE);

Solution 2

The accepted answer is fine (and I upvoted it), but it fails to use the SpannableStringBuilder as the submitter requested. As I had a case where the Builder made the most sense, here is the code for that (with a bonus use of also changing the color of the text if that is helpful to others). Note that you could also provide the initial string to the SpannableStringBuilder constructor, but I set it here to use "append" to be clear that you can append a lot before your desired "bold" text and then just record the start as shown. I would suspect that this is also faster code than the accepted answer.

SpannableStringBuilder longDescription = new SpannableStringBuilder();
longDescription.append("First Part Not Bold ");
int start = longDescription.length();
longDescription.append("BOLD");
longDescription.setSpan(new ForegroundColorSpan(0xFFCC5500), start, longDescription.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
longDescription.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), start, longDescription.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
longDescription.append(" rest not bold");

Solution 3

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

val s = SpannableStringBuilder()
        .append("First Part Not Bold ")
        .bold { append("BOLD") } 
        .append("Rest not bold")

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 s = SpannableStringBuilder()
            .color(green, { append("Green text ") })
            .append("Normal text ")
            .scale(0.5, { 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 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:1.2.0"
}

Solution 4

From API 21 SpannableStringBuilder includes a simple method to do this. Here is a solution example:

SpannableStringBuilder builder= new SpannableStringBuilder();
StyleSpan boldSpan = new StyleSpan(android.graphics.Typeface.BOLD);
builder.append("First Part Not Bold ")
              .append("BOLD ", boldSpan, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
              .append("rest not bold");

Since it is a good chance you do not support API 21 only you can duplicate the code from that method:

public SpannableStringBuilder append(CharSequence text, Object what, int flags) {
        int start = length();
        append(text);
        setSpan(what, start, length(), flags);
        return this;
}

Solution 5

This code should set to bold everything that comes inside the html bold tag. And it also deletes the tag so only the content inside is displayed.

        SpannableStringBuilder sb = new SpannableStringBuilder("this is <b>bold</b> and this is <b>bold too</b>  and this is <b>bold too, again</b>.");

        Pattern p = Pattern.compile("<b>.*?</b>", Pattern.CASE_INSENSITIVE);            
        boolean stop = false;
        while (!stop)
        {
            Matcher m = p.matcher(sb.toString());
            if (m.find()) {
                sb.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), m.start(), m.end(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
                sb.delete(m.end()-4, m.end());
                sb.delete(m.start(), m.start() + 3);
            }
            else
                stop = true;
        }

This code can also be adapted for other html style tags, such as Superscript (sup tag), etc.

        SpannableStringBuilder sb = new SpannableStringBuilder("text has <sup>superscript</sup> tag");

        Pattern p = Pattern.compile("<sup>.*?</sup>", Pattern.CASE_INSENSITIVE);            
        boolean stop = false;
        while (!stop)
        {
            Matcher m = p.matcher(sb.toString());
            if (m.find()) {
                sb.setSpan(new SuperscriptSpan(), m.start(), m.end(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
                sb.delete(m.end()-6, m.end());
                sb.delete(m.start(), m.start() + 5);
            }
            else
                stop = true;
        }

To set the color, just use the ForegroundColorSpan with setSpan.

sb.setSpan(new ForegroundColorSpan(Color.rgb(255, 0, 0)), m.start(), m.end(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);

Hope it helps.

Share:
90,442
Code Droid
Author by

Code Droid

Updated on November 14, 2021

Comments

  • Code Droid
    Code Droid over 2 years

    I need to create a String placed in a TextView that will display a string like this:

    First Part Not Bold BOLD rest not bold

    So I want to know how I could use SpannableStringBuilder to do this?

    I could use three TextEdit to accomplish this but I would like to use best solution.

  • Code Droid
    Code Droid about 12 years
    Also how can I set font size as well for say the bold? so I am specifying both that it is bold and say font 20?
  • Code Droid
    Code Droid about 12 years
    Also please update index to be (finalString.indexOf(normalBOLD),finalString.indexOf(normalB‌​OLD)+normalBOLD.lent‌​h() )
  • Phil Kulak
    Phil Kulak over 11 years
    Keep in mind that this is really slow and will likely cause you to skip frames if it's done during scrolling.
  • Noumenon
    Noumenon almost 11 years
    Besides what CodeDroid said, another issue is that the indexOf method can catch a repeated word and leave your end before your start for an IndexOutOfBoundsException. So it's better to format the substrings and then put them together.
  • Paul Brewczynski
    Paul Brewczynski over 10 years
    @hotveryspicy I suspect "finalString.indexOf(normalBOLD)" this part is - contrary to appearances - efficient, because of String interning.. Isn't it ?
  • Qw4z1
    Qw4z1 almost 9 years
    Also, by retrieving the start position of the bold part of the text before appending it, you don't have to worry about duplicate occurrences of the word that is supposed to be bold.
  • dabluck
    dabluck over 7 years
    because spannablebuilder builds one span, while spannablestringbuilder is for building charsequences with multiple different spans
  • Someone Somewhere
    Someone Somewhere over 6 years
    NOTE: android:textAllCaps="true" will break SpannableString
  • Baptiste Candellier
    Baptiste Candellier almost 5 years
    Definitely the best option if you can use Kotlin. The DSL makes it a much, much nicer API.
  • AndroidRuntimeException
    AndroidRuntimeException over 4 years
    Perfect answer, you saved my day! I have replaced the pattern by val p = Pattern.compile("<b>([^<]*)</b>", Pattern.MULTILINE or Pattern.DOTALL) because using you pattern if you have only the start tag all the text is bolded. Thanks
  • obey
    obey over 4 years
    How does it work with LiveData<String> ? I cannot seem to make it apply these changes even though everything is compiling and looks ok?
  • PangoSea
    PangoSea about 3 years
    best with kotlin
  • Hossein Farrokhi
    Hossein Farrokhi over 2 years
    back then, when Kotlin was not around, using Spannable API was a nightmare...