Skip to content

新增span支持,新增style支持,新增字体背景色 #10

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# html-text

[![](https://jitpack.io/v/wangchenyan/html-text.svg)](https://jitpack.io/#wangchenyan/html-text)
[![](https://jitpack.io/v/SilverIceKey/html-text.svg)](https://jitpack.io/#SilverIceKey/html-text)

html-text 是 android.text.Html 的一个扩展,可以加载 HTML 并将其转换成 Spannable 显示在 TextView 上,支持网络图片,图片加载器无绑定,支持图片和链接点击事件,扩展了更多标签。

Expand Down
4 changes: 3 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,18 @@
buildscript {
repositories {
jcenter()
google()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.3'
classpath 'com.android.tools.build:gradle:4.0.0'
classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5'
}
}

allprojects {
repositories {
jcenter()
google()
}
}

Expand Down
4 changes: 2 additions & 2 deletions gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#Thu May 25 10:41:46 CST 2017
#Tue Jul 21 09:34:40 CST 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
154 changes: 142 additions & 12 deletions htmltext/src/main/java/me/wcy/htmltext/HtmlTagHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,21 @@
import android.text.TextPaint;
import android.text.style.AbsoluteSizeSpan;
import android.text.style.AlignmentSpan;
import android.text.style.BackgroundColorSpan;
import android.text.style.BulletSpan;
import android.text.style.ForegroundColorSpan;
import android.text.style.LeadingMarginSpan;
import android.text.style.StrikethroughSpan;
import android.text.style.TypefaceSpan;
import android.text.style.UnderlineSpan;
import android.util.Log;
import android.widget.TextView;

import org.xml.sax.XMLReader;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;

Expand All @@ -56,6 +60,8 @@ class HtmlTagHandler implements Html.TagHandler {
private static final String LIST_ITEM = "HTML_TEXT_TAG_LI";
private static final String FONT = "HTML_TEXT_TAG_FONT";
private static final String DIV = "HTML_TEXT_TAG_DIV";
private static final String SPAN = "HTML_TEXT_TAG_SPAN";
private static final String P = "HTML_TEXT_TAG_P";

private Context mContext;
private TextPaint mTextPaint;
Expand All @@ -71,6 +77,11 @@ class HtmlTagHandler implements Html.TagHandler {
*/
private Stack<Integer> olNextIndex = new Stack<>();

/**
* Span嵌套反加载
*/
private Stack<Font> SpanFontNextIndex = new Stack<>();

private static final int indent = 10;
private static final int listItemIndent = indent * 2;
private static final BulletSpan bullet = new BulletSpan(indent);
Expand Down Expand Up @@ -108,6 +119,10 @@ String overrideTags(String html) {
html = html.replace("</font>", "</" + FONT + ">");
html = html.replace("<div", "<" + DIV);
html = html.replace("</div>", "</" + DIV + ">");
html = html.replace("<span", "<" + SPAN);
html = html.replace("</span>", "</" + SPAN + ">");
html = html.replace("<p", "<" + P);
html = html.replace("</p>", "</" + P + ">");

return html;
}
Expand Down Expand Up @@ -136,6 +151,11 @@ public void handleTag(final boolean opening, final String tag, Editable output,
}
} else if (tag.equalsIgnoreCase(FONT)) {
startFont(output, xmlReader);
} else if (tag.equalsIgnoreCase(SPAN)) {
startFont(output, xmlReader);
} else if (tag.equalsIgnoreCase(P)) {
handleDiv(output);
startFont(output, xmlReader);
} else if (tag.equalsIgnoreCase(DIV)) {
handleDiv(output);
} else if (tag.equalsIgnoreCase("code")) {
Expand Down Expand Up @@ -195,6 +215,11 @@ public void handleTag(final boolean opening, final String tag, Editable output,
}
} else if (tag.equalsIgnoreCase(FONT)) {
endFont(output);
} else if (tag.equalsIgnoreCase(SPAN)) {
endSpan(output);
} else if (tag.equalsIgnoreCase(P)) {
handleDiv(output);
endP(output);
} else if (tag.equalsIgnoreCase(DIV)) {
handleDiv(output);
} else if (tag.equalsIgnoreCase("code")) {
Expand All @@ -209,6 +234,8 @@ public void handleTag(final boolean opening, final String tag, Editable output,
end(output, Th.class, false);
} else if (tag.equalsIgnoreCase("td")) {
end(output, Td.class, false);
} else if (tag.equalsIgnoreCase("html")) {
setCss(output);
}
}
}
Expand Down Expand Up @@ -238,12 +265,20 @@ private static class Td {
}

private static class Font {
public String background_color;
public String color;
public String size;
public String text_decoration;
public String text_align;
public int where;
public int len;

public Font(String color, String size) {
public Font(String background_color, String color, String size, String text_decoration, String text_align) {
this.background_color = background_color;
this.color = color;
this.size = size;
this.text_decoration = text_decoration;
this.text_align = text_align;
}
}

Expand Down Expand Up @@ -283,29 +318,107 @@ private void end(Editable output, Class kind, boolean paragraphStyle, Object...
private void startFont(Editable output, XMLReader xmlReader) {
int len = output.length();
Map<String, String> attributes = getAttributes(xmlReader);
String background_color = attributes.get("background-color");
String color = attributes.get("color");
String size = attributes.get("size");
output.setSpan(new Font(color, size), len, len, Spannable.SPAN_MARK_MARK);
String size = attributes.get("font-size");
String text_decoration = attributes.get("text-decoration");
String text_align = attributes.get("text-align");
output.setSpan(new Font(background_color, color, size, text_decoration, text_align), len, len, Spannable.SPAN_MARK_MARK);
}

private void endFont(Editable output) {
int len = output.length();
Object obj = getLast(output, Font.class);
int where = output.getSpanStart(obj);

output.removeSpan(obj);

if (where != len) {
Font f = (Font) obj;
f.where = where;
f.len = len;
int background_color = parseColor(f.background_color);
int color = parseColor(f.color);
int size = parseSize(f.size);
String text_decoration = f.text_decoration;
if (background_color != -1) {
output.setSpan(new BackgroundColorSpan(background_color | 0xFF000000), where, len, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
if (color != -1) {
output.setSpan(new ForegroundColorSpan(color | 0xFF000000), where, len, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
if (size > 0) {
Log.d("font-size", new AbsoluteSizeSpan(size, true).getSize() + "");
output.setSpan(new AbsoluteSizeSpan(size, true), where, len, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
if ("underline".equals(text_decoration)) {
output.setSpan(new UnderlineSpan(), where, len, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
if ("line-through".equals(text_decoration)) {
output.setSpan(new StrikethroughSpan(), where, len, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
}

private void endP(Editable output) {
endSpan(output, true);
}

private void endSpan(Editable output) {
endSpan(output, false);
}

private void endSpan(Editable output, boolean isP) {
int len = output.length();
Object obj = getLast(output, Font.class);
int where = output.getSpanStart(obj);
output.removeSpan(obj);

if (where != len) {
Font f = (Font) obj;
if (isP && f.text_align == null) {
f.text_align = "left";
}
f.where = where;
f.len = len;
SpanFontNextIndex.push(f);
}
}

private void setCss(Editable output) {
while (!SpanFontNextIndex.empty()) {
Font f = SpanFontNextIndex.pop();
int background_color = parseColor(f.background_color);
int color = parseColor(f.color);
int size = parseSize(f.size);
String text_decoration = f.text_decoration;
String text_align = f.text_align;
int where = f.where;
int len = f.len;
if (background_color != -1) {
output.setSpan(new BackgroundColorSpan(background_color | 0xFF000000), where, len, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
if (color != -1) {
output.setSpan(new ForegroundColorSpan(color | 0xFF000000), where, len, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
if (size > 0) {
Log.d("font-size", new AbsoluteSizeSpan(size, true).getSize() + "");
output.setSpan(new AbsoluteSizeSpan(size, true), where, len, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
if ("underline".equals(text_decoration)) {
output.setSpan(new UnderlineSpan(), where, len, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
if ("line-through".equals(text_decoration)) {
output.setSpan(new StrikethroughSpan(), where, len, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
if ("left".equals(text_align)) {
output.setSpan(new AlignmentSpan.Standard(Layout.Alignment.ALIGN_NORMAL), where, len, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
if ("center".equals(text_align)) {
output.setSpan(new AlignmentSpan.Standard(Layout.Alignment.ALIGN_CENTER), where, len, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
if ("right".equals(text_align)) {
output.setSpan(new AlignmentSpan.Standard(Layout.Alignment.ALIGN_OPPOSITE), where, len, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
}

Expand Down Expand Up @@ -342,8 +455,18 @@ private HashMap<String, String> getAttributes(XMLReader xmlReader) {
* This is as tight as things can get :)
* The data index is "just" where the keys and values are stored.
*/
for (int i = 0; i < len; i++)
for (int i = 0; i < len; i++) {
if ("style".equals(data[i * 5 + 1]) || "data-mce-style".equals(data[i * 5 + 1])) {
String[] styleAttrs = data[i * 5 + 4].split(";");
int styleLen = styleAttrs.length;
for (int j = 0; j < styleLen; j++) {
String[] styleAttr = styleAttrs[j].split(":");
attributes.put(styleAttr[0].trim(), styleAttr[1].trim());
}
continue;
}
attributes.put(data[i * 5 + 1], data[i * 5 + 4]);
}
} catch (Exception ignored) {
}
return attributes;
Expand All @@ -367,8 +490,17 @@ private static Object getLast(Editable text, Class kind) {
}

private static int parseColor(String colorString) {
String hexColorString = "#";
try {
return Color.parseColor(colorString);
if (colorString.contains("rgb")) {
String[] rgb = colorString.replace("rgb(", "").replace(")", "").split(",");
for (int i = 0; i < 3; i++) {
hexColorString += Integer.toHexString(Integer.parseInt(rgb[i].trim()));
}
} else {
hexColorString = colorString;
}
return Color.parseColor(hexColorString);
} catch (Exception ignored) {
return -1;
}
Expand All @@ -378,19 +510,17 @@ private static int parseColor(String colorString) {
* dpValue
*/
private int parseSize(String size) {
if (size != null) {
size = size.replace("px", "").replace("pt", "");
}
int s;
try {
s = Integer.parseInt(size);
} catch (NumberFormatException ignored) {
return 0;
}

s = Math.max(s, 1);
s = Math.min(s, 7);

int baseSize = px2dp(mTextPaint.getTextSize());

return (s - 3) + baseSize;
return px2dp(s);
}

private int px2dp(float pxValue) {
Expand Down
8 changes: 4 additions & 4 deletions sample/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ android {
}

dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile project(':htmltext')
compile 'com.android.support:appcompat-v7:25.3.1'
compile 'com.github.bumptech.glide:glide:3.7.0'
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation project(':htmltext')
implementation 'com.android.support:appcompat-v7:25.4.0'
implementation 'com.github.bumptech.glide:glide:3.7.0'
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import android.support.v7.app.AppCompatActivity;
import android.text.method.LinkMovementMethod;
import android.util.DisplayMetrics;
import android.util.Log;
import android.widget.TextView;
import android.widget.Toast;

Expand Down
4 changes: 2 additions & 2 deletions sample/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@

<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp"
android:text="Hello World!" />
</ScrollView>
34 changes: 1 addition & 33 deletions sample/src/main/res/raw/sample.html
Original file line number Diff line number Diff line change
@@ -1,33 +1 @@
<h2>Hello world</h2>
<p><font size='6' color='#FF0000'>Font size</font></p>
<ul>
<li><a href="http://www.jianshu.com/users/3231579893ac">Blog</a></li>
<li><a href="https://github.com/wangchenyan">Github</a>, welcome to star or fork,
if you have issues, please tell me.
</li>
</ul>
<br/>
<ol>
<li>first</li>
<li>second
<ol>
<li>second - first
<br/>
newline
</li>
</ol>
</li>
</ol>
<br/>
<img width="200" height="200"
src="http://bp.googleblog.cn/ggpt/Su3f9QSJx6CqXkjEiOvDzSAAML6RNq9YD6kSCIPov5eudHyou61mN2trSJfydldf067uImrYOPmyFBw7DDlvNSa65vCMSqJ7LLdfcDSgdteYZjE4YQo23vaNooXyhh7xcAkCGCmJ">
<br/>
<p>
With billions of Android devices around the world, Android has surpassed our wildest
expectations. Today at Google I/O, we showcased a number of ways we’re pushing
Android forward, with the
<a href="https://developer.android.com/preview/index.html">O Release</a>, new
tools for developers to help create more performant apps, and an early preview of a
project we call Android Go -- a new experience that we’re building for entry-level
devices.
</p>
<p style="margin-bottom: 15px; text-align: center; margin-top: 25px; text-indent: 0em;"><strong><span style="font-size: 36px; color: #FF0000;">0元办会员</span></strong>,<strong><span style="font-size: 36px; color: #FF0000;">高端</span></strong>配置<strong><span style="font-size: 36px; color: #FF0000;">任你玩</span></strong></p ><p style="margin-bottom: 15px; margin-top: 25px; text-indent: 0em;"><font color="#ff0000"><span style="font-size: 14px;"><b>首充300元</b></span></font></p ><p style="margin-bottom: 15px; margin-top: 25px; text-indent: 0em; text-align: right;"><font color="#ff0000"><span style="font-size: 14px;"><b>200元<br/></b></span></font></p ><p style="margin-bottom: 15px; margin-top: 25px; text-indent: 0em;"><font color="#ff0000"><span style="font-size: 36px;"><b><br/></b></span></font></p >