Is it possible to have multiple styles inside a TextView?
Asked Answered
M

20

577

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?

Montez answered 7/10, 2009 at 1:33 Comment(2)
If the TextView string is static, you can just add html <b>, <i>, and <u> tags into the strings resource file and they will automatically be applied. E.g. <TextView android:text="@string/test" /> where @string/test is set to <string><b>bold</b>, <i>italic</i></string>Pines
+1 @greg7gkb! The key word is 'static.' I was pulling my hair out wondering why some of my strings worked with <b> and some did not. The ones that did not had variables in them.Trinatrinal
M
708

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?

Montez answered 7/10, 2009 at 18:58 Comment(10)
@JānisGruzis: Perhaps one way of doing this is through a stub method, say, formatTextWhite(string text) that just inserts the text into the following format string: "<font size="..." color="..." face="...">%s</font>".Montez
@Montez i have used <font fgcolor='#ffff5400'><b><big>" + "Title" + "</big></b> </font> but the fgcolor/color(both tried) is not working...do you know how to do the color thing using htmlSev
@MuhammadBabar: Can you try this: 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.Montez
@Montez thanks but a got a way around it Html.fromHtml("<font color=\"#999999\"> the escape sequences worked for me :)...Sev
why this doesn't work mBox..setText(Html.fromHtml("<b>" + name + "</b>") + doNotApplyHTML); any idea thanksWeymouth
@ShanXeeshi I find that using the append() function for any text I don't want to use HTML in works nicely.Paramilitary
The following code: monthYearLabel.setText(Html.fromHtml("<b>" + month.getMonthLabel().toUpperCase()) + "</b>" + " " + month.getYearLabel()); is displaying this: JUN</b> 2016 Can anyone tell me what Im doing wrong?Lurk
I assume using Html.fromHtml actually creates the exact same objects behind the scenes, of spannable stuff, right?Sand
Also note that ClickableSpan won't work without textView.movementMethod = LinkMovementMethod.getInstance()Psychoneurotic
NOTE: Html.fromHtml() is not good. when using long string.Cycling
Z
221

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);
Zoometry answered 7/10, 2009 at 13:3 Comment(13)
Great... That works... Now, if I want a different size for different texts, I'm assuming it is not right to put it in the html markup because the font tag is deprecated... Is there an alternate way to do this?Montez
Actually that gets me to another question: Is it better to have one textview with html text inside it or three text views with different markups setup without using the html class? I'm assuming its obviously the first one but just wanted to confirm it...Montez
I have no idea what the full roster of tags that Html.fromHtml() supports -- you would need to look at the source code. Inline markup and multiple TextView widgets should be orthogonal decisions. Use multiple widgets if you need precise placement of discrete bits of text. Use inline markup if you, um, need markup inline in a widget. Remember: there is no FlowLayout in Android, so stringing together multiple TextViews to create a paragraph is not truly practical AFAIK.Zoometry
Thanks for that... Actually, the <small> tag worked... So I'll keep it simple and just use it...Montez
@Zoometry well what will do for long texts. are there any tool for HTML editor for android. beacuse if I use normat HTML editor to arrenge long texts, produced source codes are changing style (bold, italc etc.) but not changing colorsWitten
@mehmet: You are welcome to manually apply spans like ForegroundColorSpan to your Spannable objects.Zoometry
I tried this but it read everything as string except the <> </> (i.e.: it shows: This mixes bold and italic stuff) .. what should I do ?Goforth
This isnt working: Spanned monthText = Html.fromHtml("<b>" + month.getMonthLabel().toUpperCase() + "</b>"); monthYearLabel.setText(monthText + " " + month.getYearLabel()); Can anyone tell me what Im doing wrong?Lurk
@TimBoland: You are throwing away your spans via monthText + " " + month.getYearLabel(). See StringUtils.concat().Zoometry
@Zoometry : Thanks CommonsWare. Are you referreing to the StringUtils in Apache Commons Lang 2.6?Lurk
@TimBoland: No, sorry, that was a typo. I meant TextUtils.concat().Zoometry
@Zoometry : Awesome. Thanks To CommonsWare. This is working for me. What do you think? - Spanned monthText = Html.fromHtml("<b>"+month.getMonthLabel().toUpperCase()+"</b>"); Spanned yearText = Html.fromHtml("&nbsp;"+month.getYearLabel()); monthYearLabel.setText(TextUtils.concat(monthText, yearText));Lurk
@TimBoland: You could do that all in one: monthYearLabel.setText(Html.fromHtml("<b>"+month.getMonthLabel().toUpperCase()+"</b>&nbsp;"+month.getYearLabel())), if you do not need the individual pieces.Zoometry
T
193

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!

Taal answered 3/11, 2011 at 11:20 Comment(3)
Usually "false" implies that you accessed a weird value on the lookup table. I got this when I had R.id.ok and R.string.ok, and accidentally used getString(R.id.ok) instead of the correct getString(R.string.ok)Airsick
Does this work in a layout-XML as well? When I refer to the string resource there with "@string/someText" (where "someText" is a resource defined in a strings.xml), I just get the string with all the HTML tags as 'text'.Roundfaced
I found this the best answer. Inspired me. Could not help but put my two-cents worth answer in too. Solution here gist.github.com/aegis1980/b138dcb2fd1b2e98aa30 allows you to assign in layout file so you do not need to assign text resource programmatically (and can preview in Android Studio!)Fosque
T
142

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);
Tattan answered 14/3, 2012 at 20:40 Comment(6)
copy/paste it to a simple android application you might be working on. You will see that on a single textview two styles will be applied.Tattan
sorry i wasn't clear. if you have to specify the substring (via the two indexes) of where to apply the style, that doesn't work for localized strings because the indexes will of course be different for every locale.Leverett
You are correct. If you are supporting multiple languages, this would not be the route you would want to take. Unless you were sure the string wouldn't change sizes...such as the application name for example.Tattan
I don't see why l10n can't work with this solution. Just get separate strings instead of just one: String firstPart = getString(R.string.line1); String secondPart = getString(R.string.line2); Spannable text = new SpannableString(firstPart + secondPart); text.setSpan(new ForegroundColorSpan(Color.parseColor(TEXT_COLOR_FOR_FIRST_PART)), 0, firstPart.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); myTextView.setText(text, TextView.BufferType.SPANNABLE);Evolutionist
@JeffreyBlattman @Evolutionist I second that, also one shouldn't be using 0, 5, 6, 10, even if they know myString's contents. (Though I support examples like this as samples!) Also see https://mcmap.net/q/49618/-is-it-possible-to-have-multiple-styles-inside-a-textview for a more generic i18n example.Cretaceous
@Evolutionist because l10n is positional as well. you can't hardcode the position of words in a phrase and still be l10n. that's why you see things like $1%s is a happy $2%s in string bundles. you have to allow the tokens to be re-arranged.Leverett
D
84

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.

Doxology answered 31/1, 2017 at 9:24 Comment(4)
Your post is better than Google Documentation about SpannableString.. Thank youEfficacy
Did this support Images which from web service. My requirement like that.Classicist
@MohanRajS SpannableStrings support Unicode emoji, but not other images.Doxology
@Doxology thank you for prompt response, but my side requirement is jpg, png. Only choice to handle is webview? :(Classicist
F
47

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:

<resource>
    <string id="@+id/styled_welcome_message">We are <b><i>so</i></b> glad to see you.</string>
</resources>

(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.

Fosque answered 2/9, 2010 at 8:57 Comment(1)
Html.fromHtml is often an easier way to style text. Also, this link is already in the original question.Lemos
M
17

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;
}
Michey answered 7/1, 2013 at 23:22 Comment(1)
I don't think this will work if a localized version of the string reorders {0} and {1}. You may have to change startLocation0 if startLocation1 is < startLocation0Decosta
I
17

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

Ichor answered 9/12, 2013 at 11:17 Comment(0)
G
11

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
Gallery answered 27/6, 2019 at 0:51 Comment(0)
A
8

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

Alkmaar answered 3/9, 2015 at 7:27 Comment(0)
P
7

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
    ]]>"
/>
Pyrrhic answered 20/8, 2014 at 0:7 Comment(1)
Does it preview in Android Studio then properly?Mertz
G
7

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

Goodwife answered 3/10, 2015 at 11:45 Comment(0)
P
3

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

Pepillo answered 14/11, 2017 at 8:0 Comment(0)
F
1

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)
Formularize answered 17/8, 2017 at 15:53 Comment(0)
W
0

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.

Wiggs answered 11/8, 2014 at 10:1 Comment(0)
F
0

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...

Frowzy answered 11/8, 2016 at 9:30 Comment(0)
H
0

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!

Hsu answered 7/6, 2017 at 23:15 Comment(0)
C
0

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>
]]>

Cristacristabel answered 18/10, 2017 at 12:46 Comment(0)
B
0

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

Barozzi answered 1/10, 2021 at 11:12 Comment(0)
K
0

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

Kvass answered 7/1, 2022 at 17:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.