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?
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: Which HTML tags are supported by Android TextView?
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);
monthText + " " + month.getYearLabel()
. See StringUtils.concat()
.
monthYearLabel.setText(Html.fromHtml("<b>"+month.getMonthLabel().toUpperCase()+"</b> "+month.getYearLabel()))
, if you do not need the individual pieces.
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> myemail@grail.com<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!
"@string/someText"
(where "someText" is a resource defined in a strings.xml), I just get the string with all the HTML tags as 'text'.
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);
$1%s is a happy $2%s
in string bundles. you have to allow the tokens to be re-arranged.
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.
https://i.stack.imgur.com/kFZ3b.png
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
Explain the meaning of Span flags like SPAN_EXCLUSIVE_EXCLUSIVE
Android Spanned, SpannedString, Spannable, SpannableString and CharSequence
Types of spans
This example was originally inspired from here.
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:
(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.
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;
}
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> "));
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) 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
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
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
]]>"
/>
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
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)
}
}
}
}
https://i.stack.imgur.com/1Gs7H.png
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)
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.
It might be as simple as leveraging the String's length() method:
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. 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...
Using an auxiliary Spannable Class as Android String Resources 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!
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> me@donut.com<br/>
<i>Donuts for life </i>
]]>
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
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:
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) 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) SpannableString.applyStyle(context: Context, vararg patternAndStyles: PatternAndStyles): This will apply the styles to the given patterns
Now, on code:
getAllIndexOf fun String.getAllIndexOf(pattern: String): List
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
Success story sharing
formatTextWhite(string text)
that just inserts the text into the following format string:"<font size="..." color="..." face="...">%s</font>"
.Html.fromHtml("<![CDATA[<font color='#ffff5400'>the html content you already have</font>]]>");
? I remember this worked sometime back for me. Not sure if it still works.Html.fromHtml("<font color=\"#999999\">
the escape sequences worked for me :)...mBox..setText(Html.fromHtml("<b>" + name + "</b>") + doNotApplyHTML);
any idea thanks