I have some HTML content with something like
<html>
<h3>nested unordered lists</h3>
<ul>
<li>
first level
<ul>
<li>
second level
<ul>
<li>
third level
<ul>
<li>
fourth level
<ul>
<li>fifth level</li>
</ul>
</li>
<li>fourth level</li>
</ul>
</li>
<li>third level</li>
</ul>
</li>
<li>second level</li>
<li>second level: this should be a long enough text that will be wrapped into multiple lines</li>
</ul>
</li>
<li>first level</li>
</ul>
<hr>
<h3>nested ordered lists</h3>
<ol>
<li>
first level
<ol>
<li>
second level
<ol>
<li>
third level
<ol>
<li>
fourth level
<ol>
<li>fifth level</li>
</ol>
</li>
<li>fourth level</li>
</ol>
</li>
<li>third level</li>
</ol>
</li>
<li>second level</li>
<li>second level: this should be a long enough text that will be wrapped into multiple lines</li>
</ol>
</li>
<li>first level</li>
</ol>
<hr>
<h3>Mixed (ol and ul) nested lists:</h3>
<ul>
<li>
first unordered
<ol>
<li>first ordered</li>
<li>
second ordered
<ul>
<li>
unordered in second ordered
<ol>
<li>ordered in "unordered in second ordered"</li>
<li>another ordered in ""unordered in second ordered"</li>
</ol>
</li>
</ul>
</li>
<li>third ordered with some other formatting: <b>bold</b> and <i>italics</i></li>
</ol>
</li>
<li>second unordered</li>
</ul>
</html>
Now I am trying to display HTML content on single Textview. Code snippet is given below
textView.setText(Html.fromHtml(htmlContent, null, htmlTagHandler));
Here
- htmlTagHandler is HtmlTagHandler reference which is used to support
<ul>
,<ol>
,<li>
tags.
HtmlTagHandler.java:
public class HtmlTagHandler implements Html.TagHandler {
/**
* Keeps track of lists (ol, ul). On bottom of Stack is the outermost list
* and on top of Stack is the most nested list
*/
Stack<String> lists = new Stack<String>();
/**
* Tracks indexes of ordered lists so that after a nested list ends
* we can continue with correct index of outer list
*/
Stack<Integer> olNextIndex = new Stack<Integer>();
/**
* List indentation in pixels. Nested lists use multiple of this.
*/
private static final int indent = 10;
private static final int listItemIndent = indent * 2;
private static final BulletSpan bullet = new BulletSpan(indent);
@Override
public void handleTag(boolean opening, String tag, Editable output, XMLReader xmlReader) {
if (tag.equalsIgnoreCase("ul")) {
if (opening) {
lists.push(tag);
} else {
lists.pop();
}
} else if (tag.equalsIgnoreCase("ol")) {
if (opening) {
lists.push(tag);
olNextIndex.push(Integer.valueOf(1)).toString();//TODO: add support for lists starting other index than 1
} else {
lists.pop();
olNextIndex.pop().toString();
}
} else if (tag.equalsIgnoreCase("li")) {
if (opening) {
if (output.length() > 0 && output.charAt(output.length() - 1) != '\n') {
output.append("\n");
}
String parentList = lists.peek();
if (parentList.equalsIgnoreCase("ol")) {
start(output, new Ol());
output.append(olNextIndex.peek().toString() + ". ");
olNextIndex.push(Integer.valueOf(olNextIndex.pop().intValue() + 1));
} else if (parentList.equalsIgnoreCase("ul")) {
start(output, new Ul());
}
} else {
if (lists.peek().equalsIgnoreCase("ul")) {
if ( output.length() > 0 && output.charAt(output.length() - 1) != '\n' ) {
output.append("\n");
}
// Nested BulletSpans increases distance between bullet and text, so we must prevent it.
int bulletMargin = indent;
if (lists.size() > 1) {
bulletMargin = indent-bullet.getLeadingMargin(true);
if (lists.size() > 2) {
// This get's more complicated when we add a LeadingMarginSpan into the same line:
// we have also counter it's effect to BulletSpan
bulletMargin -= (lists.size() - 2) * listItemIndent;
}
}
BulletSpan newBullet = new BulletSpan(bulletMargin);
end(output,
Ul.class,
new LeadingMarginSpan.Standard(listItemIndent * (lists.size() - 1)),
newBullet);
} else if (lists.peek().equalsIgnoreCase("ol")) {
if ( output.length() > 0 && output.charAt(output.length() - 1) != '\n' ) {
output.append("\n");
}
int numberMargin = listItemIndent * (lists.size() - 1);
if (lists.size() > 2) {
// Same as in ordered lists: counter the effect of nested Spans
numberMargin -= (lists.size() - 2) * listItemIndent;
}
end(output,
Ol.class,
new LeadingMarginSpan.Standard(numberMargin));
}
}
} else {
if (opening) Log.d("TagHandler", "Found an unsupported tag " + tag);
}
}
private static void start(Editable text, Object mark) {
int len = text.length();
text.setSpan(mark, len, len, Spanned.SPAN_MARK_MARK);
}
private static void end(Editable text, Class<?> kind, Object... replaces) {
int len = text.length();
Object obj = getLast(text, kind);
int where = text.getSpanStart(obj);
text.removeSpan(obj);
if (where != len) {
for (Object replace: replaces) {
text.setSpan(replace, where, len, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
return;
}
private static Object getLast(Spanned text, Class<?> kind) {
/*
* This knows that the last returned object from getSpans()
* will be the most recently added.
*/
Object[] objs = text.getSpans(0, text.length(), kind);
if (objs.length == 0) {
return null;
}
return objs[objs.length - 1];
}
private static class Ul { }
private static class Ol { }
}
Here I am able to display bullets and numbers by using Html.fromHtml(htmlContent, htmlImageGet, htmlTagHandler) but not horizontal lines. Can anyone guide me how to display horizontal line or supporting <hr>
tag in Html.fromHtml()
method by the help of HtmlTagHandler or else any other approach.
Expected output like below screen shot.
Everything is working fine expect that horizontal lines. Can anybody help on this issue.
This is how I've solved your question. It's not a very elegant solution, but it works.
Html.TagHandler:
Then create a CustomHRSpan class: