android developer android developer - 5 months ago 39
Android Question

How to have a SpannableStringBuilder append a span that's inside a formatted string?

Background



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

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

Answer

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 query=...
int 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(string, 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.