102

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.

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
Code Droid
  • 10,344
  • 17
  • 72
  • 112

11 Answers11

110
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);
Teovald
  • 4,369
  • 4
  • 26
  • 45
Mohammed Azharuddin Shaikh
  • 41,633
  • 14
  • 96
  • 115
  • 1
    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 May 31 '12 at 07:14
  • 7
    Also please update index to be (finalString.indexOf(normalBOLD),finalString.indexOf(normalBOLD)+normalBOLD.lenth() ) – Code Droid May 31 '12 at 07:30
  • 3
    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. – Noumenon Aug 26 '13 at 05:47
  • @hotveryspicy I suspect "finalString.indexOf(normalBOLD)" this part is - contrary to appearances - efficient, because of String interning.. Isn't it ? – Paweł Brewczynski Feb 20 '14 at 10:58
  • NOTE: `android:textAllCaps="true"` will break SpannableString – Someone Somewhere Oct 16 '17 at 14:00
99

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"
}
David Rawson
  • 20,912
  • 7
  • 88
  • 124
97

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");
Ziem
  • 6,579
  • 8
  • 53
  • 86
Josh
  • 1,435
  • 12
  • 19
  • 2
    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. – Qw4z1 Jul 30 '15 at 16:21
47

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

Kotlin version:

val builder = SpannableStringBuilder()
val boldSpan = StyleSpan(Typeface.BOLD)
builder.append("First Part Not Bold ")
   .append("BOLD ", boldSpan, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
   .append("rest not bold")
Gober
  • 3,632
  • 3
  • 26
  • 33
7

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.

live-love
  • 48,840
  • 22
  • 240
  • 204
  • Perfect answer, you saved my day! I have replaced the pattern by val p = Pattern.compile("([^<]*)", Pattern.MULTILINE or Pattern.DOTALL) because using you pattern if you have only the start tag all the text is bolded. Thanks – AndroidRuntimeException Oct 01 '19 at 21:40
6

Use HTML code in TextView using the Html class:

Spanned styledText = Html.fromHtml("First Part Not Bold <b>BOLD</b> rest not bold");
textView.setText(styledText);
Rajesh
  • 15,724
  • 7
  • 46
  • 95
  • 21
    Keep in mind that this is really slow and will likely cause you to skip frames if it's done during scrolling. – Phil Kulak Feb 26 '13 at 21:31
6

We can also use SpannableStringBuilder with TextAppearanceSpan to accomplish that. Follow the below steps to implement like that.

  1. Create a style in styles.xml.

<style name="BoldStyle">
   <!-- Can add other styling attributes -->
   <item name="android:textStyle">bold</item>
   ......
</style>
  1. Use the below code.
SpannableStringBuilder builder = new SpannableStringBuilder("First Part Not Bold BOLD rest not bold");
builder.setSpan(new TextAppearanceSpan(this, R.style.BoldStyle), 20, 24, 0);
((TextView)findViewById(R.id.tv7)).setText(builder);

That's it. Hope it'll help someone.

Mahendran Sakkarai
  • 8,381
  • 6
  • 44
  • 66
5

you can bold and resize a part of your string in kotlin

val s = SpannableStringBuilder()
    .append("First Part Not Bold And No Resize ")
    .bold { scale(1.5f, { append("Second Part By Bold And Resize " )}) } 
    .append("Third Part Not Bold And No Resize")

yourTextview.text = s
Reza Zavareh
  • 187
  • 3
  • 7
4

So I know this has been solved, and even as requested with SpannableStringBuilder but in the event you wanted to build a string more dynamically I figured I would put this up.

// Stuff needed
TextView DataTextView = (TextView)rootView.findViewById(R.id.DataView);
String Fields[] = {...database column names as strings... "x","y"};

String DataString = new String();   

int start,stop;     // Start and Stop of formatting

// Final Result
SpannableStringBuilder coloredString = new SpannableStringBuilder(); 

SpannableString temp;       // Small segment of colored string
for (int i =0; i < Fields.length; i++)
{
    if (database_result.containsKey(Fields[i]))  // Be sure a field exists in the ContentValues
    {
            DataString = Fields[i]+": ";
        start = DataString.length();
        DataString = DataString+ +database_result.getAsInteger(Fields[i])+" ";
        stop= DataString.length();
        temp = new SpannableString(DataString);
        temp.setSpan(new ForegroundColorSpan(Color.WHITE),start, stop, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        coloredString.append(temp);
    }   
}
DataTextView.setText(coloredString);

database_result is a ContentValues type that I constructed from the returned Cursor type of the SQL query. The only problem I had with this was at first it was only ColorSpaning the first segment. It seams that you need to declare a new ForegroundColorSpan every time you want to use one (or any other kind of span) in a loop.

Zeppie
  • 179
  • 6
1

Why would you use SpannableStringBuilder when you can use SpannableBuilder?? (https://gist.github.com/qtyq/90f9b4894069a8b3676c)

SpannableString ss = SpannableBuilder.init("First Part Not Bold BOLD rest not bold")
                                     .makeBold("BOLD")
                                     .create()
qtyq
  • 92
  • 4
  • 5
    because spannablebuilder builds one span, while spannablestringbuilder is for building charsequences with multiple different spans – dabluck Feb 27 '17 at 19:24
1

For Xamarin.Android:

SpannableStringBuilder TextoFormateado = new SpannableStringBuilder();
                TextoFormateado.Append("Not Bold");
                int start = TextoFormateado.Length();

                TextoFormateado.Append("Bold and Red");
                TextoFormateado.SetSpan(new ForegroundColorSpan(new Color(255, 0, 0, 149)), 
                    start, TextoFormateado.Length(), SpanTypes.ExclusiveExclusive);
                TextoFormateado.SetSpan(new StyleSpan(TypefaceStyle.Bold), 
                    start, TextoFormateado.Length(), SpanTypes.ExclusiveExclusive);

                TextoFormateado.Append("Not bold");

                
                TxtFinalText.TextFormatted = TextoFormateado;
KennyAli
  • 225
  • 3
  • 12