feat:Text add htmlText
This commit is contained in:
parent
e73ad1db2b
commit
9e0b3e067b
@ -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;
|
||||
|
@ -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 value,like:<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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -81,6 +81,131 @@ class TextDemo extends Panel {
|
||||
textColor: Color.BLUE,
|
||||
underline: true,
|
||||
}),
|
||||
text({
|
||||
maxLines: 0,
|
||||
maxWidth: root.width,
|
||||
htmlText: `<div>
|
||||
<h1>Supported tags by default</h1>
|
||||
<ul>
|
||||
<li>
|
||||
<h2>br</h2>
|
||||
<p>
|
||||
To break<br/>lines<br/>in a<br/>paragraph,<br/>use the br tag.
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<h2>p</h2>
|
||||
<p>This is a paragraph.</p>
|
||||
<p>This is a paragraph.</p>
|
||||
<p>Paragraph elements are defined by p tags.</p>
|
||||
<p style="color:#FF0000 text-decoration:line-through background:#eeeeee">
|
||||
Support setting background color and foreground color and underline.</p>
|
||||
</li>
|
||||
<li>
|
||||
<h2>ul</h2>
|
||||
<p>An unordered list:</p>
|
||||
<ul>
|
||||
<li>coffee</li>
|
||||
<li>tea</li>
|
||||
<li>milk</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<h2>div</h2>
|
||||
<h3>This is a header</h3>
|
||||
<p>This is a paragraph.</p>
|
||||
|
||||
<div style="color:#00FF00">
|
||||
<h3>This is a header</h3>
|
||||
<p>This is a paragraph.</p>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<h2>span</h2>
|
||||
<p><span style="color:#FF0000">some text.</span>some other text.</p>
|
||||
</li>
|
||||
<li>
|
||||
<h2>strong</h2>
|
||||
<strong>This text is strong</strong>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<h2>b</h2>
|
||||
<p>This is plain text <b>This is bold text</b>。</p>
|
||||
</li>
|
||||
<li>
|
||||
<h2>em</h2>
|
||||
<em>This text is emphasized</em>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<h2>cite</h2>
|
||||
<cite>This text is cite</cite>
|
||||
</li>
|
||||
<li>
|
||||
<h2>dfn</h2>
|
||||
<dfn>This text is dfn</dfn>
|
||||
</li>
|
||||
<li>
|
||||
<h2>i</h2>
|
||||
<i>Italic</i>
|
||||
</li>
|
||||
<li>
|
||||
<h2>big</h2>
|
||||
<big>This text is big</big>
|
||||
</li>
|
||||
<li>
|
||||
<h2>small</h2>
|
||||
<small>This text is small</small>
|
||||
</li>
|
||||
<li>
|
||||
<h2>font</h2>
|
||||
<p><font color="red" size=30>This is some text!</font></p>
|
||||
<p><font color="blue">This is some text!</font></p>
|
||||
<p><font color="green">This is some text!</font></p>
|
||||
</li>
|
||||
<li>
|
||||
<h1>blockquote</h1>
|
||||
Here comes a long quotation:
|
||||
<blockquote>
|
||||
This is a long quotation. This is a long quotation. This is a long quotation. This
|
||||
is a long quotation. This is a long quotation.
|
||||
</blockquote>
|
||||
Please note that the browser adds line breaks before and after the blockquote element and increases the margins.
|
||||
</li>
|
||||
<li>
|
||||
<h1>tt</h1>
|
||||
</li>
|
||||
<li>
|
||||
<h1>a</h1>
|
||||
<a href="https://m.baidu.com">Click anchor</a>
|
||||
</li>
|
||||
<li>
|
||||
<h1>u</h1>
|
||||
<u>Underline</u>
|
||||
</li>
|
||||
<li>
|
||||
<h1>strike,s,del</h1>
|
||||
<strike>This text is strike</strike>
|
||||
<s>This text is s</s>
|
||||
<del>This text is del</del>
|
||||
</li>
|
||||
|
||||
<li>h1-h6</li>
|
||||
<h1>h1</h1>
|
||||
<h2>h2</h2>
|
||||
<h3>h3</h3>
|
||||
<h4>h4</h4>
|
||||
<h5>h5</h5>
|
||||
<h6>h6</h6>
|
||||
<li>
|
||||
<h1>img</h1>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
`
|
||||
}),
|
||||
],
|
||||
{
|
||||
space: 10,
|
||||
|
@ -126,6 +126,12 @@ - (void)blendView:(UILabel *)view forPropName:(NSString *)name propValue:(id)pro
|
||||
self.strikethrough = prop;
|
||||
[self reloadParagraphStyle];
|
||||
}];
|
||||
} else if ([name isEqualToString:@"htmlText"]) {
|
||||
NSAttributedString *attStr = [[NSAttributedString alloc] initWithData:[prop dataUsingEncoding:NSUnicodeStringEncoding]
|
||||
options:@{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType}
|
||||
documentAttributes:nil
|
||||
error:nil];
|
||||
view.attributedText = attStr;
|
||||
} else {
|
||||
[super blendView:view forPropName:name propValue:prop];
|
||||
}
|
||||
@ -142,6 +148,9 @@ - (NSMutableParagraphStyle *)ensureParagraphStyle {
|
||||
- (void)reloadParagraphStyle {
|
||||
NSString *labelText = self.view.text ?: @"";
|
||||
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:labelText];
|
||||
if (self.view.attributedText) {
|
||||
[attributedString setAttributedString:self.view.attributedText];
|
||||
}
|
||||
[attributedString addAttribute:NSParagraphStyleAttributeName value:self.paragraphStyle range:NSMakeRange(0, [labelText length])];
|
||||
if (self.underline) {
|
||||
[attributedString addAttribute:NSUnderlineStyleAttributeName value:self.underline range:NSMakeRange(0, [labelText length])];
|
||||
|
@ -1690,6 +1690,10 @@ var Text = /** @class */ (function (_super) {
|
||||
Property,
|
||||
__metadata$3("design:type", Boolean)
|
||||
], Text.prototype, "underline", void 0);
|
||||
__decorate$3([
|
||||
Property,
|
||||
__metadata$3("design:type", String)
|
||||
], Text.prototype, "htmlText", void 0);
|
||||
return Text;
|
||||
}(View));
|
||||
function text(config) {
|
||||
|
@ -1258,6 +1258,10 @@ __decorate$3([
|
||||
Property,
|
||||
__metadata$3("design:type", Boolean)
|
||||
], Text.prototype, "underline", void 0);
|
||||
__decorate$3([
|
||||
Property,
|
||||
__metadata$3("design:type", String)
|
||||
], Text.prototype, "htmlText", void 0);
|
||||
function text(config) {
|
||||
const ret = new Text;
|
||||
ret.layoutConfig = layoutConfig().fit();
|
||||
|
@ -2717,6 +2717,10 @@ __decorate$3([
|
||||
Property,
|
||||
__metadata$3("design:type", Boolean)
|
||||
], Text.prototype, "underline", void 0);
|
||||
__decorate$3([
|
||||
Property,
|
||||
__metadata$3("design:type", String)
|
||||
], Text.prototype, "htmlText", void 0);
|
||||
function text(config) {
|
||||
const ret = new Text;
|
||||
ret.layoutConfig = layoutConfig().fit();
|
||||
|
2
doric-js/index.d.ts
vendored
2
doric-js/index.d.ts
vendored
@ -486,6 +486,7 @@ declare module 'doric/lib/src/widget/text' {
|
||||
lineSpacing?: number;
|
||||
strikethrough?: boolean;
|
||||
underline?: boolean;
|
||||
htmlText?: string;
|
||||
}
|
||||
export class Text extends View implements IText {
|
||||
text?: string;
|
||||
@ -500,6 +501,7 @@ declare module 'doric/lib/src/widget/text' {
|
||||
lineSpacing?: number;
|
||||
strikethrough?: boolean;
|
||||
underline?: boolean;
|
||||
htmlText?: string;
|
||||
}
|
||||
export function text(config: IText): Text;
|
||||
}
|
||||
|
2
doric-js/lib/src/widget/text.d.ts
vendored
2
doric-js/lib/src/widget/text.d.ts
vendored
@ -14,6 +14,7 @@ export interface IText extends IView {
|
||||
lineSpacing?: number;
|
||||
strikethrough?: boolean;
|
||||
underline?: boolean;
|
||||
htmlText?: string;
|
||||
}
|
||||
export declare class Text extends View implements IText {
|
||||
text?: string;
|
||||
@ -28,5 +29,6 @@ export declare class Text extends View implements IText {
|
||||
lineSpacing?: number;
|
||||
strikethrough?: boolean;
|
||||
underline?: boolean;
|
||||
htmlText?: string;
|
||||
}
|
||||
export declare function text(config: IText): Text;
|
||||
|
@ -76,6 +76,10 @@ __decorate([
|
||||
Property,
|
||||
__metadata("design:type", Boolean)
|
||||
], Text.prototype, "underline", void 0);
|
||||
__decorate([
|
||||
Property,
|
||||
__metadata("design:type", String)
|
||||
], Text.prototype, "htmlText", void 0);
|
||||
export function text(config) {
|
||||
const ret = new Text;
|
||||
ret.layoutConfig = layoutConfig().fit();
|
||||
|
@ -31,6 +31,7 @@ export interface IText extends IView {
|
||||
lineSpacing?: number
|
||||
strikethrough?: boolean
|
||||
underline?: boolean
|
||||
htmlText?: string
|
||||
}
|
||||
|
||||
export class Text extends View implements IText {
|
||||
@ -69,6 +70,9 @@ export class Text extends View implements IText {
|
||||
|
||||
@Property
|
||||
underline?: boolean
|
||||
|
||||
@Property
|
||||
htmlText?: string
|
||||
}
|
||||
|
||||
export function text(config: IText) {
|
||||
|
4
doric-web/dist/index.js
vendored
4
doric-web/dist/index.js
vendored
@ -2775,6 +2775,10 @@ __decorate$3([
|
||||
Property,
|
||||
__metadata$3("design:type", Boolean)
|
||||
], Text.prototype, "underline", void 0);
|
||||
__decorate$3([
|
||||
Property,
|
||||
__metadata$3("design:type", String)
|
||||
], Text.prototype, "htmlText", void 0);
|
||||
function text(config) {
|
||||
const ret = new Text;
|
||||
ret.layoutConfig = layoutConfig().fit();
|
||||
|
2
doric-web/dist/index.js.map
vendored
2
doric-web/dist/index.js.map
vendored
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user