HTML class
All that interests us is inside the class android.text.Html. If you have enough patience you can go through it yourself, it’s just around 850 lines of code.
What you basically do is a call of Html.fromHtml(String source)
static method passing your text wrapped with HTML tags. In return, you get an object that can be passed as a parameter in TextView.setText()
method. For example:
Spanned parsedText = Html.fromHtml("<font color='#444444' face='sans-serif-medium'>Some colored</font> text"); textView.setText(parsedText);
What is going on under the hood of Html.fromHtml(String source)
is:
- String with HTML tags is parsed into java object using SAX parser
- Java objects are converted into spans
That’s it. Now you may ask, what are spans? It’s a name of a powerful feature for making rich text views on Android. It’s a big mystery for me why Google is not providing any comprehensive documentation. Although, I was already writing about that here. Most of the time, as a point of reference, I use this post from Flavien Laurent.
HTML To Spanned Converter
Above I described a general overview. To get more details we have to focus on a class HtmlToSpannedConverter
. This is the implementation of ContentHandler
for mentioned SAX parser. We are mostly interested in 2 inherited methods:
1. startElement()
This is called for each opening tag and location of the span that is marked. For some tags e.g. font some extra properties are stored to read later.
Default start handling:
private static void start(SpannableStringBuilder text, Object mark) { int len = text.length(); text.setSpan(mark, len, len, Spannable.SPAN_MARK_MARK); }
Font start handling:
private static void startFont(SpannableStringBuilder text, Attributes attributes) { String color = attributes.getValue("", "color"); String face = attributes.getValue("", "face"); int len = text.length(); text.setSpan(new Font(color, face), len, len, Spannable.SPAN_MARK_MARK); }
2. endElement()
This is called for each closing tag. Function search for starting span, keep starting location, remove start span and then apply proper span for a tag.
Default end handling:
private static void end(SpannableStringBuilder text, Class kind, Object repl) { int len = text.length(); Object obj = getLast(text, kind); int where = text.getSpanStart(obj); text.removeSpan(obj); if (where != len) { text.setSpan(repl, where, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } }
Supported tags
It is very easy to list all supported HTML tags just by looking at handleEndTag
method. If you expect some properties being read from a specific tag, check which method is called for it inside handleEndTag
, e.g. endFont
is called for font tag.
There we go:
- br – no need to explain 🙂
- p, div – apply line space
- strong, b – apply BOLD style span
- em, cite, dfn, i – apply ITALIC style span
- big – apply 1.25 size factor
- small – apply 0.8 size factor
- blockquote
- tt – apply monospace typeface
- a
- u – apply underline span
- sup – apply superscript span
- sub – apply subscript span
- h1 to h6
Image handler
Now it’s a time to confess to a little lie 🙂 There is one special tag that is handled only in startElement
method. It’s img!
To make that tag work we have to provide ImageGetter
when calling Html.fromHtml(String source, ImageGetter imageGetter, TagHandler tagHandler)
.
It is a very simple interface with one method to implement:
Drawable getDrawable(String source)
Once img tag is found, ImageSpan
span is applied with drawable retrieved thanks to our ImageGetter
. If the image getter or the drawable are not found, the Android will use a built-in drawable for unknown images.
Tag handler
Except those built-in tag handlers, we can easily add our own tag handling. As you saw above there is Html.fromHtml(String source, ImageGetter imageGetter, TagHandler tagHandler)
where we can provide our TagHandler
implementation.
Please note that both imageGetter and tagHandler are optional, you can always use null
for one of them.
The TagHandler
interface is very simple. There is just one method to implement:
public void handleTag(boolean opening, String tag, Editable output, XMLReader xmlReader);
You should know that output parameter is actually SpannableStringBuilder
inside the implementation. So looking at other tags handling should give you easy start with implementing your own handler.
The end
As you saw, using HTML in TextView is very simple under the hood, there is no magic.