Is it possible to have multiple styles inside a TextView?

AndroidStylesTextview

Android Problem Overview


Is it possible to set multiple styles for different pieces of text inside a TextView?

For instance, I am setting the text as follows:

tv.setText(line1 + "\n" + line2 + "\n" + word1 + "\t" + word2 + "\t" + word3);

Is it possible to have a different style for each text element? E.g., line1 bold, word1 italic, etc.

The developer guide's Common Tasks and How to Do Them in Android includes Selecting, Highlighting, or Styling Portions of Text:

> // Get our EditText object. EditText vw = (EditText)findViewById(R.id.text); > // Set the EditText's text. vw.setText("Italic, highlighted, bold."); > // If this were just a TextView, we could do: // vw.setText("Italic, highlighted, bold.", TextView.BufferType.SPANNABLE); // to force it to use Spannable storage so styles can be attached. // Or we could specify that in the XML. > // Get the EditText's internal text storage Spannable str = vw.getText(); > // Create our span sections, and assign a format to each. str.setSpan(new StyleSpan(android.graphics.Typeface.ITALIC), 0, 7, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); str.setSpan(new BackgroundColorSpan(0xFFFFFF00), 8, 19, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); str.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), 21, str.length() - 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

But that uses explicit position numbers inside the text. Is there a cleaner way to do this?

Android Solutions


Solution 1 - Android

In case, anyone is wondering how to do this, here's one way: (Thanks to Mark again!)

mBox = new TextView(context);
mBox.setText(Html.fromHtml("<b>" + title + "</b>" +  "<br />" + 
			"<small>" + description + "</small>" + "<br />" + 
			"<small>" + DateAdded + "</small>"));

For an unofficial list of tags supported by this method, refer to this link or this question: https://stackoverflow.com/questions/9754076/which-html-tags-are-supported-by-android-textview

Solution 2 - Android

Try Html.fromHtml(), and mark up your text with bold and italic HTML tags e.g:

Spanned text = Html.fromHtml("This mixes <b>bold</b> and <i>italic</i> stuff");
textView.setText(text);

Solution 3 - Android

Slightly off-topic, but I found this too useful not to be mentioned here.

What if we would like to read the the Html text from string.xml resource and thus make it easy to localize. CDATA make this possible:

<string name="my_text">
  <![CDATA[
    <b>Autor:</b> Mr Nice Guy<br/>
    <b>Contact:</b> [email protected]<br/>
    <i>Copyright © 2011-2012 Intergalactic Spacebar Confederation </i>
  ]]>
</string> 

From our Java code we could now utilize it like this:

TextView tv = (TextView) findViewById(R.id.myTextView);
tv.setText(Html.fromHtml(getString(R.string.my_text))); 

I did not expect this to work. But it did.

Hope it's useful to some of you!

Solution 4 - Android

If you don't feel like using html, you could just create a styles.xml and use it like this:

TextView tv = (TextView) findViewById(R.id.textview);
SpannableString text = new SpannableString(myString);

text.setSpan(new TextAppearanceSpan(getContext(), R.style.myStyle), 0, 5, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
text.setSpan(new TextAppearanceSpan(getContext(), R.style.myNextStyle), 6, 10, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

tv.setText(text, TextView.BufferType.SPANNABLE);

Solution 5 - Android

It is more light weight to use a SpannableString instead of html markup. It helps me to see visual examples so here is a supplemental answer.

enter image description here

This is a single TextView.

// set the text
SpannableString s1 = new SpannableString("bold\n");
SpannableString s2 = new SpannableString("italic\n");
SpannableString s3 = new SpannableString("foreground color\n");
SpannableString s4 = new SpannableString("background color\n");
SpannableString s5 = new SpannableString("underline\n");
SpannableString s6 = new SpannableString("strikethrough\n");
SpannableString s7 = new SpannableString("bigger\n");
SpannableString s8 = new SpannableString("smaller\n");
SpannableString s9 = new SpannableString("font\n");
SpannableString s10 = new SpannableString("URL span\n");
SpannableString s11 = new SpannableString("clickable span\n");
SpannableString s12 = new SpannableString("overlapping spans\n");

// set the style
int flag = Spanned.SPAN_EXCLUSIVE_EXCLUSIVE;
s1.setSpan(new StyleSpan(Typeface.BOLD), 0, s1.length(), flag);
s2.setSpan(new StyleSpan(Typeface.ITALIC), 0, s2.length(), flag);
s3.setSpan(new ForegroundColorSpan(Color.RED), 0, s3.length(), flag);
s4.setSpan(new BackgroundColorSpan(Color.YELLOW), 0, s4.length(), flag);
s5.setSpan(new UnderlineSpan(), 0, s5.length(), flag);
s6.setSpan(new StrikethroughSpan(), 0, s6.length(), flag);
s7.setSpan(new RelativeSizeSpan(2), 0, s7.length(), flag);
s8.setSpan(new RelativeSizeSpan(0.5f), 0, s8.length(), flag);
s9.setSpan(new TypefaceSpan("monospace"), 0, s9.length(), flag);
s10.setSpan(new URLSpan("https://developer.android.com"), 0, s10.length(), flag);
s11.setSpan(new ClickableSpan() {
    @Override
    public void onClick(View widget) {
        Toast.makeText(getApplicationContext(), "Span clicked", Toast.LENGTH_SHORT).show();
    }
}, 0, s11.length(), flag);
s12.setSpan(new ForegroundColorSpan(Color.RED), 0, 11, flag);
s12.setSpan(new BackgroundColorSpan(Color.YELLOW), 4, s12.length(), flag);
s12.setSpan(new UnderlineSpan(), 4, 11, flag);

// build the string
SpannableStringBuilder builder = new SpannableStringBuilder();
builder.append(s1);
builder.append(s2);
builder.append(s3);
builder.append(s4);
builder.append(s5);
builder.append(s6);
builder.append(s7);
builder.append(s8);
builder.append(s9);
builder.append(s10);
builder.append(s11);
builder.append(s12);

// set the text view with the styled text
textView.setText(builder);
// enables clicking on spans for clickable span and url span
textView.setMovementMethod(LinkMovementMethod.getInstance());

#Further Study

This example was originally inspired from here.

Solution 6 - Android

The list of supported tags is:

> If you use a string resource, you can add some simple styling, such as bold or italic using HTML notation. The currently supported tags are: B (bold), I (italic), U (underline), TT (monospace), BIG, SMALL, SUP (superscript), SUB (subscript), and STRIKE (strikethrough). So, for example, in res/values/strings.xml you could declare this: > > > We are so glad to see you. >

(From http://developer.android.com/guide/faq/commontasks.html#selectingtext — Web Archive link, <resource> typo is in original!)

It also shows that Html.fromHtml isn't really needed in simple cases.

Solution 7 - Android

I was running into the same problem. I could use fromHtml, but I am android now, not web, so I decided to try this out. I do have to localize this though so I gave it a shot using string replacement concept. I set the style on the TextView to be the main style, then just format the other peices.

I hope this helps others looking to do the same thing - I don't know why this isn't easier in the framework.

My strings look like this:


<string name="my_text">{0} You will need a {1} to complete this assembly</string>
<string name="text_sub0">1:</string>
<string name="text_sub1">screwdriver, hammer, and measuring tape</string>

Here are the styles:


<style name="MainStyle">
<item name="android:textSize">@dimen/regular_text</item>
<item name="android:textColor">@color/regular_text</item>
</style>
<style name="style0">
<item name="android:textSize">@dimen/paragraph_bullet</item>
<item name="android:textColor">@color/standout_text</item>
<item name="android:textStyle">bold</item>
</style>
<style name="style1">
<item name="android:textColor">@color/standout_light_text</item>
<item name="android:textStyle">italic</item>
</style>

Here is my code that calls my formatStyles method:


SpannableString formattedSpan = formatStyles(getString(R.string.my_text), getString(R.string.text_sub0), R.style.style0, getString(R.string.main_text_sub1), R.style.style1);
textView.setText(formattedSpan, TextView.BufferType.SPANNABLE);

The format method:


private SpannableString formatStyles(String value, String sub0, int style0, String sub1, int style1)
{
String tag0 = "{0}";
int startLocation0 = value.indexOf(tag0);
value = value.replace(tag0, sub0);



String tag1 = "{1}";
int startLocation1 = value.indexOf(tag1);
if (sub1 != null && !sub1.equals(""))
{
    value = value.replace(tag1, sub1);
}

SpannableString styledText = new SpannableString(value);
styledText.setSpan(new TextAppearanceSpan(getActivity(), style0), startLocation0, startLocation0 + sub0.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
if (sub1 != null && !sub1.equals(""))
{
    styledText.setSpan(new TextAppearanceSpan(getActivity(), style1), startLocation1, startLocation1 + sub1.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}

return styledText;




}

}

Solution 8 - Android

Now the <b> element is deprecated. <strong> renders as <b>, and <em> renders as <i>.

tv.setText(Html.fromHtml("<strong>bold</strong> and <em>italic</em> "));
   

this works fine for me

Solution 9 - Android

Yes, it is possible using SpannedString. If you are using Kotlin, it becomes even easier to do by using core-ktx, as it provides a [domain-specific-language (DSL)](https://developer.android.com/reference/kotlin/androidx/core/text/package-summary#buildspannedstring "https://developer.android.com/reference/kotlin/androidx/core/text/package-summary#buildspannedstring";) for doing this:

    val string: SpannedString = buildSpannedString {
        bold {
            append("1111")
        }
        append("Devansh")     
    }

More options provided by it are:

append("Hello There")
bold {
    append("bold")
    italic {
        append("bold and italic")
        underline {
            append("then some text with underline")
        }
    }
}

At last, you can just to:

textView.text = string

Solution 10 - Android

Here is an easy way to do so using HTMLBuilder

    myTextView.setText(new HtmlBuilder().
                    open(HtmlBuilder.Type.BOLD).
                    append("Some bold text ").
                    close(HtmlBuilder.Type.BOLD).
                    open(HtmlBuilder.Type.ITALIC).
                    append("Some italic text").
                    close(HtmlBuilder.Type.ITALIC).
                    build()
    );

Result:

Some bold text Some italic text

Solution 11 - Android

If you want to be able to add the styled text in xml you can create a custom view extending TextView and override setText():

public class HTMLStyledTextView extends TextView
{
    public HTMLStyledTextView(Context context) {
        super(context);
    }

    public HTMLStyledTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public HTMLStyledTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public void setText(CharSequence text, BufferType type)
    {
       super.setText(Html.fromHtml(text.toString()), type);
    }
}

Then, you can use it like this (replace PACKAGE_NAME with your package name):

<PACKAGE_NAME.HTMLStyledTextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="<![CDATA[
        <b>Bolded Text:</b> Non-Bolded Text
    ]]>"
/>

Solution 12 - Android

As stated, use TextView.setText(Html.fromHtml(String))

And use these tags in your Html formatted string:

<a href="...">
<b>
<big>
<blockquote>
<br>
<cite>
<dfn>
<div align="...">
<em>
<font size="..." color="..." face="...">
<h1>
<h2>
<h3>
<h4>
<h5>
<h6>
<i>
<img src="...">
<p>
<small>
<strike>
<strong>
<sub>
<sup>
<tt>
<u>

http://commonsware.com/blog/Android/2010/05/26/html-tags-supported-by-textview.html

Solution 13 - Android

Me Too

How about using some beautiful markup with Kotlin and Anko -

import org.jetbrains.anko.*
override fun onCreate(savedInstanceState: Bundle?) {
    title = "Created with Beautiful Markup"
    super.onCreate(savedInstanceState)

    verticalLayout {
        editText {
            hint = buildSpanned {
                append("Italic, ", Italic)
                append("highlighted", backgroundColor(0xFFFFFF00.toInt()))
                append(", Bold", Bold)
            }
        }
    }
}

Created with Beautiful Markup

Solution 14 - Android

Spanny make SpannableString easier to use.

Spanny spanny = new Spanny("Underline text", new UnderlineSpan())
                .append("\nRed text", new ForegroundColorSpan(Color.RED))
                .append("\nPlain text");
textView.setText(spanny)

Solution 15 - Android

In fact, except the Html object, you also could use the Spannable type classes, e.g. TextAppearanceSpan or TypefaceSpan and SpannableString togather. Html class also uses these mechanisms. But with the Spannable type classes, you've more freedom.

Solution 16 - Android

It might be as simple as leveraging the String's length() method:

  1. Split the text string in the Strings XML file into as many sub-strings (a seperate strings from Android's point of view) as many you need different styles, so it could be like: str1, str2, str3 (as in your case), which when joined together are the whole single string you use.

  2. And then simply follow the "Span" method, just like you presented with your code - but instead of a single string, combine all the substrings merging them into a single one, each with a different custom style.

You still use the numbers, however not directly - they're no more take a hardcoded form (as in your code) now, but they're being substituted with the combined length() methods (note two stars preceding and suffixing the str.length() in place of the absolute number to extuinguish the change):

str.setSpan(new StyleSpan(android.graphics.Typeface.ITALIC), 0, **str.length()**, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);

for the first string size, then str.length() + 1, str.length() + str2.length() for the second string size, and so on with all the substrings, instead of e.g. 0,7 or 8,19 and so on...

Solution 17 - Android

Using an auxiliary Spannable Class as [Android String Resources][1] shares at the bottom of the webpage. You can approach this by creatingCharSquences and giving them a style.

But in the example they give us, is just for bold, italic, and even colorize text. I needed to wrap several styles in aCharSequence in order to set them in a TextView. So to that Class (I named it CharSequenceStyles ) I just added this function.

public static CharSequence applyGroup(LinkedList<CharSequence> content){
    SpannableStringBuilder text = new SpannableStringBuilder();
    for (CharSequence item : content) {
        text.append(item);
    }
    return text;
}

And in the view I added this.

            message.push(postMessageText);
            message.push(limitDebtAmount);
            message.push(pretMessageText);
            TextView.setText(CharSequenceStyles.applyGroup(message));

I hope this help you! [1]: https://developer.android.com/guide/topics/resources/string-resource.html

Solution 18 - Android

As Jon said, for me this is the best solution and you dont need to set any text at runtime, only use this custom class HtmlTextView

public class HtmlTextView extends TextView {

  public HtmlTextView(Context context) {
      super(context);
  }

  public HtmlTextView(Context context, AttributeSet attrs) {
      super(context, attrs);
  }

  public HtmlTextView(Context context, AttributeSet attrs, int defStyleAttr) 
  {
      super(context, attrs, defStyleAttr);
  }

  @TargetApi(21)
  public HtmlTextView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
      super(context, attrs, defStyleAttr, defStyleRes);
  }

  @Override
  public void setText(CharSequence s,BufferType b){
      super.setText(Html.fromHtml(s.toString()),b);
  }

}

and thats it, now only put it in your XML

<com.fitc.views.HtmlTextView
    android:id="@+id/html_TV"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/example_html" />

with your Html String

<string name="example_html">
<![CDATA[
<b>Author:</b> Mr Donuthead<br/>
<b>Contact:</b> [email protected]<br/>
<i>Donuts for life </i>
]]>

Solution 19 - Android

The cleanest way in Kotlin is by using Span

val myTitleText = "Hello World"

val spannable = SpannableString(myTitleText)
spannable.setSpan(
    TextAppearanceSpan(context, R.style.myFontMedium),
    0,
    4,
    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)
tvMytitle.text = spannable

From: Hello Word

To: Hello World

Solution 20 - Android

Using SpannableString is a good way to achieve that

I use a few functions to make it easy to apply, I will explain the idea of each first and then show the code:

  1. String.getAllIndexOf(pattern: String): This will search the patter on the string and return an index list of where the pattern start. Ex: given the string "abcdefabc" and I call the method passing the "abc" as the searched pattern, the method should return the list: listOf(0, 6)
  2. The is a class to receive the pattern and a list of styles (in case you desire to apply different styles to the same pattern in sequence)
  3. SpannableString.applyStyle(context: Context, vararg patternAndStyles: PatternAndStyles): This will apply the styles to the given patterns


Now, on code:

  1. getAllIndexOf

    fun String.getAllIndexOf(pattern: String): List<Int> {
    	val allRecordsOfText = mutableListOf<Int>()
    
    	var index = 0
    	while(index >= 0) {
    		val newStart = if (allRecordsOfText.isEmpty()) {
    			0
    		} else {
    			allRecordsOfText.last() + pattern.length
    		}
    		index = this.subSequence(newStart, this.length).indexOf(pattern)
    
    		if (index >= 0) {
    			allRecordsOfText.add(newStart + index)
    		}
    	}
    
    	return allRecordsOfText.toList()
    }
    
  2. Class to receive the pattern and styles

    @Parcelize
    class PatternAndStyles(
    	val pattern: String,
    	val styles: List<Int>
    ) : Parcelable
    
  3. applyStyle

    fun SpannableString.applyStyle(context: Context, vararg patternAndStyles: PatternAndStyles) {
    	for (patternStyle in patternAndStyles.toList()) {
    
    		this.toString().getAllIndexOf(patternStyle.pattern).forEachIndexed { index, start ->
    			val end = start + patternStyle.pattern.length
    			val styleIndex = if (patternStyle.styles.size > index) index else patternStyle.styles.size - 1
    
    			this.setSpan(
    				TextAppearanceSpan(context, patternStyle.styles[styleIndex]),
    				start,
    				end,
    				SPAN_EXCLUSIVE_EXCLUSIVE
    			)
    		}
    	}
    }
    
  4. How to use it in the end

    val stringToApplyStyle = "abc def abc def"
    val text = SpannableString(stringToApplyStyle)
    text.applyStyle(
    	this.applicationContext,
    	PatternAndStyles("abc", listOf(R.style.Style1, R.style.Style2)),
    	PatternAndStyles("def", listOf(R.style.Style3))
    )
    
  5. The output:
    styles applied to text

As it was passed to styles to the pattern "abc" both of then were used But on the pattern "def" the second record reused the last style given on the list

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionLegendView Question on Stackoverflow
Solution 1 - AndroidLegendView Answer on Stackoverflow
Solution 2 - AndroidCommonsWareView Answer on Stackoverflow
Solution 3 - AndroidBenView Answer on Stackoverflow
Solution 4 - AndroidKent AndersenView Answer on Stackoverflow
Solution 5 - AndroidSuragchView Answer on Stackoverflow
Solution 6 - AndroidJonView Answer on Stackoverflow
Solution 7 - AndroidfarcratsView Answer on Stackoverflow
Solution 8 - AndroidSandeep PView Answer on Stackoverflow
Solution 9 - AndroidDevansh MauryaView Answer on Stackoverflow
Solution 10 - AndroidIlya GazmanView Answer on Stackoverflow
Solution 11 - AndroidbcorsoView Answer on Stackoverflow
Solution 12 - AndroidAndrew GallaschView Answer on Stackoverflow
Solution 13 - AndroidHimanshuView Answer on Stackoverflow
Solution 14 - AndroidDesmond LuaView Answer on Stackoverflow
Solution 15 - AndroidClock ZHONGView Answer on Stackoverflow
Solution 16 - AndroidforsbergView Answer on Stackoverflow
Solution 17 - AndroidMontDeskaView Answer on Stackoverflow
Solution 18 - AndroidAWolfsdorfView Answer on Stackoverflow
Solution 19 - AndroidThiagoView Answer on Stackoverflow
Solution 20 - AndroidM. MassulaView Answer on Stackoverflow