feat:Text add htmlText

This commit is contained in:
pengfei.zhou
2020-04-14 11:44:22 +08:00
committed by osborn
parent e73ad1db2b
commit 9e0b3e067b
14 changed files with 437 additions and 1 deletions

View File

@@ -16,6 +16,8 @@
package pub.doric.shader;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.text.Html;
import android.text.TextUtils;
import android.util.TypedValue;
import android.view.Gravity;
@@ -26,6 +28,8 @@ import com.github.pengfeizhou.jscore.JSValue;
import pub.doric.DoricContext;
import pub.doric.extension.bridge.DoricPlugin;
import pub.doric.shader.richtext.CustomTagHandler;
import pub.doric.shader.richtext.HtmlParser;
import pub.doric.utils.DoricUtils;
/**
@@ -153,6 +157,19 @@ public class TextNode extends ViewNode<TextView> {
view.getPaint().setUnderlineText(prop.asBoolean().value());
}
break;
case "htmlText":
if (prop.isString()) {
view.setText(
HtmlParser.buildSpannedText(prop.asString().value(),
new Html.ImageGetter() {
@Override
public Drawable getDrawable(String source) {
return null;
}
},
new CustomTagHandler()));
}
break;
default:
super.blend(view, name, prop);
break;

View File

@@ -0,0 +1,115 @@
package pub.doric.shader.richtext;
/*
* Copyright [2019] [Doric.Pub]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import android.text.Editable;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.AbsoluteSizeSpan;
import org.xml.sax.Attributes;
import java.util.Stack;
/**
* @Description: pub.doric.shader.richtext
* @Author: pengfei.zhou
* @CreateDate: 2020-04-14
*/
public class CustomTagHandler implements HtmlParser.TagHandler {
private Stack<Integer> startIndex = new Stack<>();
/**
* html attribute valuelike:<size value='16'></size>
*/
private Stack<String> propertyValue = new Stack<>();
@Override
public boolean handleTag(boolean opening, String tag, Editable output, Attributes attributes) {
if (opening) {
handleStartTag(tag, output, attributes);
} else {
handleEndTag(tag, output, attributes);
}
return false;
}
private void handleStartTag(String tag, Editable output, Attributes attributes) {
if (tag.equalsIgnoreCase("font")) {
handleStartFont(output, attributes);
}
}
private void handleEndTag(String tag, Editable output, Attributes attributes) {
if (tag.equalsIgnoreCase("font")) {
handleEndFont(output);
}
}
private void handleStartFont(Editable output, Attributes attributes) {
startIndex.push(output.length());
propertyValue.push(HtmlParser.getValue(attributes, "size"));
}
/**
* <font size='4'></font>
* * * 1-9
* * * 2-10
* * * 3-12
* * * 4-14
* * * 5-18
* * * 6-24
* * * 7-36
*/
private void handleEndFont(Editable output) {
String val = propertyValue.pop();
if (!TextUtils.isEmpty(val)) {
int value = 12;
try {
value = Integer.parseInt(val);
} catch (Exception e) {
e.printStackTrace();
}
switch (value) {
case 1:
value = 9;
break;
case 2:
value = 10;
break;
case 4:
value = 14;
break;
case 5:
value = 18;
break;
case 6:
value = 24;
break;
case 7:
value = 36;
break;
default:
value = 12;
break;
}
output.setSpan(new AbsoluteSizeSpan(value, true), startIndex.pop(), output.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
}

View File

@@ -0,0 +1,142 @@
package pub.doric.shader.richtext;
/*
* Copyright [2019] [Doric.Pub]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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;
/**
* @Description: pub.doric.shader.richtext
* @Author: pengfei.zhou
* @CreateDate: 2020-04-14
*/
public class HtmlParser implements Html.TagHandler, ContentHandler {
//This approach has the advantage that it allows to disable processing of some tags while using default processing for others,
// e.g. you can make sure that ImageSpan objects are not created:
public interface TagHandler {
// return true here to indicate that this tag was handled and
// should not be processed further
boolean handleTag(boolean opening, String tag, Editable output, Attributes attributes);
}
public static Spanned buildSpannedText(String html, Html.ImageGetter imageGetter, TagHandler handler) {
return Html.fromHtml("<inject/>" + html, imageGetter, 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 mInnerContentHandler;
private Editable mEditable;
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 (mInnerContentHandler == null) {
// record result object
mEditable = output;
// record current content handler
mInnerContentHandler = xmlReader.getContentHandler();
// replace content handler with our own that forwards to calls to original when needed
xmlReader.setContentHandler(this);
// handle endElement() callback for <inject/> tag
tagStatus.addLast(Boolean.FALSE);
}
}
@Override
public void setDocumentLocator(Locator locator) {
mInnerContentHandler.setDocumentLocator(locator);
}
@Override
public void startDocument() throws SAXException {
mInnerContentHandler.startDocument();
}
@Override
public void endDocument() throws SAXException {
mInnerContentHandler.endDocument();
}
@Override
public void startPrefixMapping(String prefix, String uri) throws SAXException {
mInnerContentHandler.startPrefixMapping(prefix, uri);
}
@Override
public void endPrefixMapping(String prefix) throws SAXException {
mInnerContentHandler.endPrefixMapping(prefix);
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
boolean isHandled = handler.handleTag(true, localName, mEditable, attributes);
tagStatus.addLast(isHandled);
if (!isHandled) {
mInnerContentHandler.startElement(uri, localName, qName, attributes);
}
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if (!tagStatus.removeLast()) {
mInnerContentHandler.endElement(uri, localName, qName);
}
handler.handleTag(false, localName, mEditable, null);
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
mInnerContentHandler.characters(ch, start, length);
}
@Override
public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {
mInnerContentHandler.ignorableWhitespace(ch, start, length);
}
@Override
public void processingInstruction(String target, String data) throws SAXException {
mInnerContentHandler.processingInstruction(target, data);
}
@Override
public void skippedEntity(String name) throws SAXException {
mInnerContentHandler.skippedEntity(name);
}
}