使用TagHandler
提供的XmlReader
可以获取标记属性值,而无需使用反射,但该方法比反射更加复杂。诀窍是将XmlReader
使用的ContentHandler
替换为自定义对象。只能在调用handleTag()
时替换ContentHandler
。这会导致获取第一个标记的属性值时出现问题,可以通过在HTML开头添加自定义标记来解决。
import android.text.Editable;
import android.text.Html;
import android.text.Spanned;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import java.util.ArrayDeque;
public class HtmlParser implements Html.TagHandler, ContentHandler
{
public interface TagHandler
{
boolean handleTag(boolean opening, String tag, Editable output, Attributes attributes);
}
public static Spanned buildSpannedText(String html, TagHandler handler)
{
return Html.fromHtml("<inject/>" + html, null, new HtmlParser(handler));
}
public static String getValue(Attributes attributes, String name)
{
for (int i = 0, n = attributes.getLength(); i < n; i++)
{
if (name.equals(attributes.getLocalName(i)))
return attributes.getValue(i);
}
return null;
}
private final TagHandler handler;
private ContentHandler wrapped;
private Editable text;
private ArrayDeque<Boolean> tagStatus = new ArrayDeque<>();
private HtmlParser(TagHandler handler)
{
this.handler = handler;
}
@Override
public void handleTag(boolean opening, String tag, Editable output, XMLReader xmlReader)
{
if (wrapped == null)
{
text = output;
wrapped = xmlReader.getContentHandler();
xmlReader.setContentHandler(this);
tagStatus.addLast(Boolean.FALSE);
}
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes)
throws SAXException
{
boolean isHandled = handler.handleTag(true, localName, text, attributes);
tagStatus.addLast(isHandled);
if (!isHandled)
wrapped.startElement(uri, localName, qName, attributes);
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException
{
if (!tagStatus.removeLast())
wrapped.endElement(uri, localName, qName);
handler.handleTag(false, localName, text, null);
}
@Override
public void setDocumentLocator(Locator locator)
{
wrapped.setDocumentLocator(locator);
}
@Override
public void startDocument() throws SAXException
{
wrapped.startDocument();
}
@Override
public void endDocument() throws SAXException
{
wrapped.endDocument();
}
@Override
public void startPrefixMapping(String prefix, String uri) throws SAXException
{
wrapped.startPrefixMapping(prefix, uri);
}
@Override
public void endPrefixMapping(String prefix) throws SAXException
{
wrapped.endPrefixMapping(prefix);
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException
{
wrapped.characters(ch, start, length);
}
@Override
public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException
{
wrapped.ignorableWhitespace(ch, start, length);
}
@Override
public void processingInstruction(String target, String data) throws SAXException
{
wrapped.processingInstruction(target, data);
}
@Override
public void skippedEntity(String name) throws SAXException
{
wrapped.skippedEntity(name);
}
}
使用这个类来读取属性很容易:
HtmlParser.buildSpannedText("<x id=1 value=a>test<x id=2 value=b>", new HtmlParser.TagHandler()
{
@Override
public boolean handleTag(boolean opening, String tag, Editable output, Attributes attributes)
{
if (opening && tag.equals("x"))
{
String id = HtmlParser.getValue(attributes, "id");
String value = HtmlParser.getValue(attributes, "value");
}
return false;
}
});
这种方法的优点在于它允许禁用某些标签的处理,同时对其他标签使用默认处理方式,例如,您可以确保不创建ImageSpan
对象:
Spanned result = HtmlParser.buildSpannedText("<b><img src=nothing>test</b><img src=zilch>",
new HtmlParser.TagHandler()
{
@Override
public boolean handleTag(boolean opening, String tag, Editable output, Attributes attributes)
{
return tag.equals("img");
}
});
TagHandler
在哪里?通常使用SAX2的方式是使用ContentHandler
,不是吗? - Ray ToalTagHandler
用于通过Html.fromHtml(String, ImageGetter, TagHandler)
将 HTML 文本转换为可跨度文本时使用。它用于处理未知标签(即 TagSoup 不识别的标签)。 - Jason Robinson