最近做一个项目经常用到富文本去显示页面,但是富文本修改颜色样式显示不出效果,结果发现后端返回的颜色样式是rgb的格式,Android解析不出这格式,先看下两段一样颜色格式不同展现的效果
//直接使用TextView显示富文本
TextView.setText(Html.fromHtml("富文本内容"));
<p>
<span style="font-size: 14px;">检测到少量色斑,应预防紫外线照射问题,</span>
<span style="font-size: 14px; color: #ff0000;">做好肌</span>
<span style="font-size: 14px;">肤补水保湿工作,合理的休息,可以让新陈代谢有效地消除脸部色斑。</span>
</p>
<p>
<span style="font-size: 14px;">检测到少量色斑,应预防紫外线照射问题,</span>
<span style="font-size: 14px; color: rgb(230, 0, 0);">做好肌</span>
<span style="font-size: 14px;">肤补水保湿工作,合理的休息,可以让新陈代谢有效地消除脸部色斑。</span>
</p>
在不需要后端修改格式的前提下提供一个解决方式, 在别的博客中找到的方法,想链接一下博客地址,但找不到了
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Color;
import android.text.Editable;
import android.text.Html;
import android.text.Spannable;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.AbsoluteSizeSpan;
import android.text.style.BackgroundColorSpan;
import android.text.style.ForegroundColorSpan;
import android.text.style.TextAppearanceSpan;
import android.util.Log;
import com.djm.smallappliances.utils.DisplayUtils;
import org.xml.sax.XMLReader;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
/**
* created by MMF
* on 2020/9/18
* 解決富文本返回颜色格式rgb(230, 0, 0)解析不了问题
*/
public class CustomTagHandler implements Html.TagHandler {
final HashMap<String, String> attributes = new HashMap<String, String>();
private final String TAG = "CustomTagHandler";
private int startIndex = 0;
private int stopIndex = 0;
private ColorStateList mOriginColors;
private Context mContext;
public CustomTagHandler(Context context, ColorStateList originColors) {
mContext = context;
mOriginColors = originColors;
}
@Override
public void handleTag(boolean opening, String tag, Editable output, XMLReader xmlReader) {
processAttributes(xmlReader);
//你颜色样式所在的标签
if (tag.equalsIgnoreCase("myspan")) {
if (opening) {
startSpan(tag, output, xmlReader);
} else {
endSpan(tag, output, xmlReader);
attributes.clear();
}
}
}
private void processAttributes(final XMLReader xmlReader) {
try {
Field elementField = xmlReader.getClass().getDeclaredField("theNewElement");
elementField.setAccessible(true);
Object element = elementField.get(xmlReader);
Field attsField = element.getClass().getDeclaredField("theAtts");
attsField.setAccessible(true);
Object atts = attsField.get(element);
Field dataField = atts.getClass().getDeclaredField("data");
dataField.setAccessible(true);
String[] data = (String[]) dataField.get(atts);
Field lengthField = atts.getClass().getDeclaredField("length");
lengthField.setAccessible(true);
int len = (Integer) lengthField.get(atts);
/**
* MSH: Look for supported attributes and add to hash map.
* 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++) {
attributes.put(data[i * 5 + 1], data[i * 5 + 4]);
}
} catch (Exception e) {
}
}
public void startSpan(String tag, Editable output, XMLReader xmlReader) {
startIndex = output.length();
}
public void endSpan(String tag, Editable output, XMLReader xmlReader) {
stopIndex = output.length();
String color = attributes.get("color");
String bacgroundColor = attributes.get("background-color");
String size = attributes.get("size");
String style = attributes.get("style");
if (!TextUtils.isEmpty(style)) {
analysisStyle(startIndex, stopIndex, output, style);
}
if (!TextUtils.isEmpty(size)) {
size = size.split("px")[0];
}
if (!TextUtils.isEmpty(bacgroundColor)) {
if (bacgroundColor.startsWith("@")) {
Resources res = Resources.getSystem();
String name = bacgroundColor.substring(1);
int colorRes = res.getIdentifier(name, "color", "android");
if (colorRes != 0) {
output.setSpan(new BackgroundColorSpan(colorRes), startIndex, stopIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
} else {
if (bacgroundColor.startsWith("rgb")) {
bacgroundColor = bacgroundColor.replace("rgb(", "");
bacgroundColor = bacgroundColor.replace(")", "");
String[] rgbs = bacgroundColor.split(", ");
bacgroundColor = toHex(Integer.parseInt(rgbs[0]), Integer.parseInt(rgbs[1]), Integer.parseInt(rgbs[2]));
}
try {
output.setSpan(new BackgroundColorSpan(Color.parseColor(bacgroundColor)), startIndex, stopIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
} catch (Exception e) {
e.printStackTrace();
reductionFontColor(startIndex, stopIndex, output);
}
}
}
if (!TextUtils.isEmpty(color)) {
if (color.startsWith("@")) {
Resources res = Resources.getSystem();
String name = color.substring(1);
int colorRes = res.getIdentifier(name, "color", "android");
if (colorRes != 0) {
output.setSpan(new ForegroundColorSpan(colorRes), startIndex, stopIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
} else {
try {
output.setSpan(new ForegroundColorSpan(Color.parseColor(color)), startIndex, stopIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
} catch (Exception e) {
e.printStackTrace();
reductionFontColor(startIndex, stopIndex, output);
}
}
}
if (!TextUtils.isEmpty(size)) {
int fontSizePx = 16;
if (null != mContext) {
fontSizePx = DisplayUtils.dip2sp(mContext, Integer.parseInt(size));
}
output.setSpan(new AbsoluteSizeSpan(fontSizePx), startIndex, stopIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
/**
* 解析style属性
*
* @param startIndex
* @param stopIndex
* @param editable
* @param style
*/
private void analysisStyle(int startIndex, int stopIndex, Editable editable, String style) {
Log.e(TAG, "style:" + style);
String[] attrArray = style.split(";");
Map<String, String> attrMap = new HashMap<>();
if (null != attrArray) {
for (String attr : attrArray) {
String[] keyValueArray = attr.split(":");
if (null != keyValueArray && keyValueArray.length == 2) {
// 记住要去除前后空格
attrMap.put(keyValueArray[0].trim(), keyValueArray[1].trim());
}
}
}
String color = attrMap.get("color");
String bacgroundColor = attrMap.get("background-color");
String fontSize = attrMap.get("font-size");
if (!TextUtils.isEmpty(fontSize)) {
fontSize = fontSize.split("px")[0];
}
if (!TextUtils.isEmpty(bacgroundColor)) {
if (bacgroundColor.startsWith("@")) {
Resources res = Resources.getSystem();
String name = bacgroundColor.substring(1);
int colorRes = res.getIdentifier(name, "color", "android");
if (colorRes != 0) {
editable.setSpan(new BackgroundColorSpan(colorRes), startIndex, stopIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
} else {
if (bacgroundColor.startsWith("rgb")) {
bacgroundColor = bacgroundColor.replace("rgb(", "");
bacgroundColor = bacgroundColor.replace(")", "");
String[] rgbs = bacgroundColor.split(", ");
bacgroundColor = toHex(Integer.parseInt(rgbs[0]), Integer.parseInt(rgbs[1]), Integer.parseInt(rgbs[2]));
}
try {
editable.setSpan(new BackgroundColorSpan(Color.parseColor(bacgroundColor)), startIndex, stopIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
} catch (Exception e) {
e.printStackTrace();
reductionFontColor(startIndex, stopIndex, editable);
}
}
}
if (!TextUtils.isEmpty(color)) {
if (color.startsWith("@")) {
Resources res = Resources.getSystem();
String name = color.substring(1);
int colorRes = res.getIdentifier(name, "color", "android");
if (colorRes != 0) {
editable.setSpan(new ForegroundColorSpan(colorRes), startIndex, stopIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
} else {
if (color.startsWith("rgb")) {
color = color.replace("rgb(", "");
color = color.replace(")", "");
String[] rgbs = color.split(", ");
color = toHex(Integer.parseInt(rgbs[0]), Integer.parseInt(rgbs[1]), Integer.parseInt(rgbs[2]));
}
try {
editable.setSpan(new ForegroundColorSpan(Color.parseColor(color)), startIndex, stopIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
} catch (Exception e) {
e.printStackTrace();
reductionFontColor(startIndex, stopIndex, editable);
}
}
}
if (!TextUtils.isEmpty(fontSize)) {
int fontSizePx = 14;
if (null != mContext) {
fontSizePx = DisplayUtils.dip2sp(mContext, Integer.parseInt(fontSize));
}
editable.setSpan(new AbsoluteSizeSpan(fontSizePx), startIndex, stopIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
/**
* 还原为原来的颜色
*
* @param startIndex
* @param stopIndex
* @param editable
*/
private void reductionFontColor(int startIndex, int stopIndex, Editable editable) {
if (null != mOriginColors) {
editable.setSpan(new TextAppearanceSpan(null, 0, 0, mOriginColors, null),
startIndex, stopIndex,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
} else {
editable.setSpan(new ForegroundColorSpan(0xff2b2b2b), startIndex, stopIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
public static String toHex(int r, int g, int b) {
return "#" + toBrowserHexValue(r) + toBrowserHexValue(g)
+ toBrowserHexValue(b);
}
private static String toBrowserHexValue(int number) {
StringBuilder builder = new StringBuilder(
Integer.toHexString(number & 0xff));
while (builder.length() < 2) {
builder.append("0");
}
return builder.toString().toUpperCase();
}
}
使用方式:
String htmlStr = "富文本内容".replace("span", "myspan");
TextView.setText(Html.fromHtml(htmlStr, null, new CustomTagHandler(mContext, holder.tvCause.getTextColors())));
Android8.0还是9.0会过滤span标签导致handleTag方法监听不到span标签,所以修改为"myspan"(具体Android版本查清再来更新),“myspan”这个标签可以按自己想的去定义,定义后在CustomTagHandler中的handleTag监听中的判断要改成自己定义的标签