Android – How to have a SpannableStringBuilder append a span that’s inside a formatted string

android, format-string, spannablestring, spannablestringbuilder, textview


Suppose I use SpannableStringBuilder to append multiple stuff into it, and one of them is string that I format from the strings.xml file, which has a span inside:

SpannableStringBuilder stringBuilder = new SpannableStringBuilder ();stringBuilder.append(...) SpannableString span = new SpannableString(...);span.setSpan(new BackgroundColorSpan(0xff990000), ...,...,Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);stringBuilder.append(getString(R.string.string_to_format, span));stringBuilder.append(...)...textView.setText(stringBuilder);

The problem

Sadly, formatting such a string removes the span itself, so in my case, there won't be any text with a background color.

This happens on the line of the "getString".

What I've tried

If I just append the span alone (without "getString"), it works fine.

I also tried to investigate Html.fromHtml, but it doesn't seem to support a background color for text anyway.

The question

Is it possible to format a string that has a span, yet still have the span within?

More specifically, the input is a string A from the strings.xml file, which only has a placeholder (no special HTML tags), and another string B that is supposed to replace the placeholder at runtime. The string B should have a highlight for a partial text of itself.

In my case, the highlighted text is a something to search for within string B.

Best Solution

OK, I've found an answer to my special end case, but I'd still like to know if there are better ways.

Here's what I did:

String stringToSearchAt=...String queryIdx = stringToSearchAt.toLowerCase().indexOf(query);stringToSearchAt= stringToSearchAt.substring(0,  queryIdx + query.length()) + "<bc/>" + stringToSearchAt.substring(queryIdx + query.length());final String formattedStr=getString(..., stringToSearchAt);stringBuilder.append(Html.fromHtml(formattedStr, null, new TagHandler() {                                    int start;                                    @Override                                    public void handleTag(final boolean opening, final String tag, Editable output, final XMLReader xmlReader) {                                        switch (tag) {                                            case "bc":                                                if (!opening)                                                    start = output.length() - query.length();                                                break;                                            case "html":                                                if (!opening)                                                    output.setSpan(new BackgroundColorSpan(0xff00bbaa), start, start + query.length(), 0);                                        }                                    }                                }));

This is only good for my case, but in the case of general formatting, this won't suffice.