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

11

104

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.

Oliverolivera answered 31/5, 2012 at 6:0 Comment(0)
D
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);
Destalinization answered 31/5, 2012 at 6:26 Comment(5)
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?Oliverolivera
Also please update index to be (finalString.indexOf(normalBOLD),finalString.indexOf(normalBOLD)+normalBOLD.lenth() )Oliverolivera
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.Yaakov
@hotveryspicy I suspect "finalString.indexOf(normalBOLD)" this part is - contrary to appearances - efficient, because of String interning.. Isn't it ?Cestus
NOTE: android:textAllCaps="true" will break SpannableStringBranson
A
100

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"
}
Afloat answered 27/4, 2018 at 1:4 Comment(4)
Definitely the best option if you can use Kotlin. The DSL makes it a much, much nicer API.Whitefly
How does it work with LiveData<String> ? I cannot seem to make it apply these changes even though everything is compiling and looks ok?Mikimikihisa
best with kotlinMaitund
back then, when Kotlin was not around, using Spannable API was a nightmare...Outboard
R
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");
Randle answered 1/5, 2014 at 1:0 Comment(1)
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.Socle
N
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")
Neoplasticism answered 21/3, 2016 at 13:7 Comment(1)
In your above, wouldn't "int start = 0" and "int end = text.length()"?Pullulate
S
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.

Stenotype answered 5/8, 2015 at 20:35 Comment(1)
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. ThanksNatation
D
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);
Disjoined answered 31/5, 2012 at 6:20 Comment(1)
Keep in mind that this is really slow and will likely cause you to skip frames if it's done during scrolling.Simplify
Z
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.

Zarathustra answered 18/6, 2017 at 8:12 Comment(0)
T
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
Thirlage answered 17/10, 2021 at 11:8 Comment(0)
I
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.

Incentive answered 24/12, 2014 at 12:54 Comment(0)
L
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()
Lindsaylindsey answered 10/10, 2016 at 14:4 Comment(1)
because spannablebuilder builds one span, while spannablestringbuilder is for building charsequences with multiple different spansBoudicca
B
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;
Balcom answered 4/3, 2021 at 1:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.