Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Combining Spannable with String.format()

Suppose you have the following string:

String s = "The cold hand reaches for the %1$s %2$s Ellesse's"; String old = "old";  String tan = "tan";  String formatted = String.format(s,old,tan); //"The cold hand reaches for the old tan Ellesse's" 

Suppose you want to end up with this string, but also have a particular Span set for any word replaced by String.format.

For instance, we also want to do the following:

Spannable spannable = new SpannableString(formatted); spannable.setSpan(new StrikethroughSpan(), oldStart, oldStart+old.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); spannable.setSpan(new ForegroundColorSpan(Color.BLUE), tanStart, tanStart+tan.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 

Is there a robust way of getting to know the start indices of old and tan?

Note that just searching for 'old' returns the 'old' in 'cold', so that won't work.

What will work, I guess, is searching for %[0-9]$s beforehand, and calculating the offsets to account for the replacements in String.format. This seems like a headache though, I suspect there might be a method like String.format that is more informative about the specifics of its formatting. Well, is there?

like image 401
Maarten Avatar asked Jan 05 '14 17:01

Maarten


People also ask

What is a difference between Spannable and string?

A Spannable allows to attach formatting information like bold, italic, ... to sub-sequences ("spans", thus the name) of the characters. It can be used whenever you want to represent "rich text". The Html class provides an easy way to construct such text, for example: Html.

How do you set up text Spannable?

Apply a span by calling setSpan(Object what, int start, int end, int flags) on the Spannable object. The what Object is the marker that will be applied from a start to an end index in the text.

How do you do Spannable text on Android?

To apply a span, call setSpan(Object _what_, int _start_, int _end_, int _flags_) on a Spannable object. The what parameter refers to the span to apply to the text, while the start and end parameters indicate the portion of the text to which to apply the span.


2 Answers

I have created a version of String.format that works with spannables. Download it and use it just like the normal version. In your case you would put the spans around the format specifiers (possibly using strings.xml). In the output, they would be around whatever those specifiers were replaced with.

like image 108
George Steel Avatar answered Oct 13 '22 08:10

George Steel


Using Spannables like that is a headache -- this is probably the most straightforward way around:

String s = "The cold hand reaches for the %1$s %2$s Ellesse's"; String old = "<font color=\"blue\">old</font>";  String tan = "<strike>tan</strike>";  String formatted = String.format(s,old,tan); //The cold hand reaches for the <font color="blue">old</font> <strike>tan</strike> Ellesse's  Spannable spannable = Html.fromHtml(formatted); 

Problem: this does not put in a StrikethroughSpan. To make the StrikethroughSpan, we borrow a custom TagHandler from this question.

Spannable spannable = Html.fromHtml(text,null,new MyHtmlTagHandler()); 

MyTagHandler:

public class MyHtmlTagHandler implements Html.TagHandler {     public void handleTag(boolean opening, String tag, Editable output,                           XMLReader xmlReader) {         if (tag.equalsIgnoreCase("strike") || tag.equals("s")) {             processStrike(opening, output);         }     }     private void processStrike(boolean opening, Editable output) {         int len = output.length();         if (opening) {             output.setSpan(new StrikethroughSpan(), len, len, Spannable.SPAN_MARK_MARK);         } else {             Object obj = getLast(output, StrikethroughSpan.class);             int where = output.getSpanStart(obj);             output.removeSpan(obj);             if (where != len) {                 output.setSpan(new StrikethroughSpan(), where, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);             }         }     }      private Object getLast(Editable text, Class kind) {         Object[] objs = text.getSpans(0, text.length(), kind);         if (objs.length == 0) {             return null;         } else {             for (int i = objs.length; i > 0; i--) {                 if (text.getSpanFlags(objs[i - 1]) == Spannable.SPAN_MARK_MARK) {                     return objs[i - 1];                 }             }             return null;         }     } } 
like image 21
Maarten Avatar answered Oct 13 '22 08:10

Maarten