解决Android富文本无法显示rgb字体颜色和字体大小

问题

服务器返回数据:

<p>特价!特价!特价!特价优惠:<span style="color: rgb(225, 60, 57);font-size:22px;">【10元】</span>一件</p>

安卓移动端代码:


       val htmlStr = "<p>特价!特价!特价!特价优惠:<span style=\"color: rgb(225, 60, 57);font-size:22px;\">【10元】</span>一件</p>"
        findViewById<TextView>(R.id.tv_content).text = HtmlCompat.fromHtml(htmlStr, HtmlCompat.FROM_HTML_MODE_COMPACT)
        

安卓移动端显示:

在这里插入图片描述

至此,可以看出问题所在,【10元】字体颜色和大小都有问题。

如何解决?

自定义标签,并使用Html.TagHandler进行解析和属性修改
提供了Kotlin和Java两个版本,可以直接复制使用

HtmlTagHandler.kt

class HtmlTagHandler(private val tag: String?, private val context: Context) : Html.TagHandler{

    private var startIndex : Int? = 0
    private val attributes = HashMap<String, String>()

    override fun handleTag(opening: Boolean, tag: String?, output: Editable?, xmlReader: XMLReader?) {
        if (tag == this@HtmlTagHandler.tag) {
            parseAttributes(xmlReader)
            if (opening) {
                startHandleTag(output)
            } else {
                endHandleTag(output)
                attributes.clear()
            }
        }
    }

    private fun parseAttributes(xmlReader: XMLReader?) {
        try {
            val elementField: Field? = xmlReader?.javaClass?.getDeclaredField("theNewElement")
            elementField?.isAccessible = true
            val element: Any? = elementField?.get(xmlReader)
            val attsField: Field? = element?.javaClass?.getDeclaredField("theAtts")
            attsField?.isAccessible = true
            val atts: Any? = attsField?.get(element)
            val dataField: Field? = atts?.javaClass?.getDeclaredField("data")
            dataField?.isAccessible = true
            val data = dataField?.get(atts) as Array<*>
            val lengthField: Field = atts.javaClass.getDeclaredField("length")
            lengthField.isAccessible = true
            val len = lengthField.get(atts) as Int
            for (i in 0 until len) {
                attributes[data[i * 5 + 1] as String] = data[i * 5 + 4] as String
            }
        } catch (e: Exception) {
        }
    }

    private fun startHandleTag(output: Editable?) {
        startIndex = output?.length
    }

    private fun endHandleTag(output: Editable?) {
        val stopIndex = output?.length
        val style : String? = attributes["style"]
        if (!TextUtils.isEmpty(style)) {
            analysisStyle(startIndex, stopIndex, output, style)
        }
    }

    /**
     * 解析style属性
     *
     * @param startIndex startIndex
     * @param stopIndex stopIndex
     * @param editable editable
     * @param style style
     */
    private fun analysisStyle(startIndex: Int?, stopIndex: Int?, editable: Editable?, style: String?) {
        val attrArray = style?.split(";")
        val attrMap = HashMap<String, String>()
        if (attrArray != null) {
            for (attr in attrArray) {
                val keyValueArray = attr.split(":")
                if (keyValueArray.size == 2) {
                    // 去除前后空格
                    attrMap[keyValueArray[0].trim()] = keyValueArray[1].trim()
                }
            }
        }

        // 字体颜色
        var color = attrMap["color"]
        if (!TextUtils.isEmpty(color)) {
            if (color!!.startsWith("rgb")) {
                color = color.replace("rgb(", "")
                color = color.replace(")", "")
                val rgbs = color.split(", ")
                if (rgbs.size > 2) {
                    color = toHex(rgbs[0].toInt(), rgbs[1].toInt(), rgbs[2].toInt())
                }
            }
            try {
                editable?.setSpan(
                    ForegroundColorSpan(Color.parseColor(color)),
                    startIndex!!, stopIndex!!, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
                )
            } catch (e: java.lang.Exception) {
                e.printStackTrace()
            }
        }
        // 字体大小
        var fontSize = attrMap["font-size"]
        if (!TextUtils.isEmpty(fontSize)) {
            fontSize = fontSize!!.split("px")[0]
            try {
                val absoluteSize = sp2px(context, fontSize.toInt())
                editable?.setSpan(
                    AbsoluteSizeSpan(absoluteSize), startIndex!!,
                    stopIndex!!, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
            } catch (e: java.lang.Exception) {
                e.printStackTrace()
            }
        }
    }

    companion object {
        private fun toHex(r : Int, g : Int, b : Int) : String {
            return "#" + toBrowserHexValue(r) + toBrowserHexValue(g) + toBrowserHexValue(b)
        }

        private fun toBrowserHexValue(number: Int) : String {
            val builder = StringBuilder(number.and(0xff).toString(16))
            while (builder.length < 2) {
                builder.append("0")
            }
            return builder.toString().uppercase(Locale.getDefault())
        }

        private fun sp2px(context: Context, pxValue: Int) : Int {
            val fontScale = context.resources.displayMetrics.scaledDensity
            return (pxValue * fontScale + 0.5f).toInt()
        }
    }
}

HtmlTagHandler.java

public class HtmlTagHandler implements Html.TagHandler {

    private final String tag;
    private final Context context;
    private final HashMap<String, String> attributes = new HashMap<>();
    private int startIndex = 0;

    public HtmlTagHandler(String tag, Context context) {
        this.tag = tag;
        this.context = context;
    }

    @Override
    public void handleTag(boolean opening, String tag, Editable output, XMLReader xmlReader) {
        if (this.tag.equalsIgnoreCase(tag)) {
            processAttributes(xmlReader);
            if (opening) {
                startHandleTag(output);
            } else {
                endHandleTag(output);
                attributes.clear();
            }
        }
    }

    private void processAttributes(XMLReader xmlReader) {
        try {
            Field elementField = xmlReader.getClass().getDeclaredField("theNewElement");
            elementField.setAccessible(true);
            Object element = elementField.get(xmlReader);
            assert element != null;
            Field attsField = element.getClass().getDeclaredField("theAtts");
            attsField.setAccessible(true);
            Object atts = attsField.get(element);
            assert atts != null;
            Field dataField = atts.getClass().getDeclaredField("data");
            dataField.setAccessible(true);
            String[] data = (String[]) dataField.get(atts);
            Field lengthField = atts.getClass().getDeclaredField("length");
            lengthField.setAccessible(true);
            Object attsLength = lengthField.get(atts);
            int len = attsLength == null ? 0 : (Integer) attsLength;
            for (int i = 0; i < len; i++) {
                assert data != null;
                attributes.put(data[i * 5 + 1], data[i * 5 + 4]);
            }

        } catch (Exception ignored) {}
    }

    private void startHandleTag(Editable output) {
        startIndex = output.length();
    }

    private void endHandleTag(Editable output) {
        int stopIndex = output.length();
        String style = attributes.get("style");
        if (!TextUtils.isEmpty(style)) {
            assert style != null;
            analysisStyle(startIndex, stopIndex, output, style);
        }
    }

    /**
     * 解析style属性
     *
     * @param startIndex startIndex
     * @param stopIndex stopIndex
     * @param editable editable
     * @param style style
     */
    private void analysisStyle(int startIndex, int stopIndex, Editable editable, String style) {
        String[] attrArray = style.split(";");
        Map<String, String> attrMap = new HashMap<>();
        for (String attr : attrArray) {
            String[] keyValueArray = attr.split(":");
            if (keyValueArray.length == 2) {
                // 去除前后空格
                attrMap.put(keyValueArray[0].trim(), keyValueArray[1].trim());
            }
        }
        String color = attrMap.get("color");
        if (!TextUtils.isEmpty(color)) {
            assert color != null;
            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();
            }
        }
        String fontSize = attrMap.get("font-size");
        if (!TextUtils.isEmpty(fontSize)) {
            assert fontSize != null;
            fontSize = fontSize.split("px")[0];
            try {
                int size = sp2px(context, Integer.parseInt(fontSize));
                editable.setSpan(new AbsoluteSizeSpan(size), startIndex, stopIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


    private 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();
    }

    private static int sp2px(Context context, float spValue) {
        final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
        return (int) (spValue * fontScale + 0.5f);
    }
}

代码使用:


        var htmlStr = "<p>特价!特价!特价!特价优惠:<span style=\"color: rgb(225, 60, 57);font-size:22px;\">【10元】</span>一件</p>"
        htmlStr = HtmlTagUtil.replaceSpan(htmlStr)
        findViewById<TextView>(R.id.tv_content).text = HtmlCompat
            .fromHtml(htmlStr, HtmlCompat.FROM_HTML_MODE_COMPACT, null, HtmlTagHandler(HtmlTagUtil.NEW_SPAN, this))

HtmlTagUtil.kt

object HtmlTagUtil {
    const val NEW_SPAN = "newSpan"
    private const val OLD_SPAN = "span"
    fun replaceSpan(content: String): String {
        return content.replace(OLD_SPAN, NEW_SPAN)
    }
}

安卓移动端显示:

在这里插入图片描述

好了,这篇文章源代码比较多,非常感谢你能看到最后。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
你可以使用Python中的`python-docx`库来操作Word文档。具体来说,你需要使用`python-docx`库中的`Document`类来打开和操作Word文档,并使用`python-docx`库中的其他类和方法来查找文本位置、字体大小、字体名称和字体颜色。以下是一个简单的例子,演示如何使用`python-docx`库来查找Word文档中的文本位置、字体大小、字体名称和字体颜色: ```python from docx import Document # 打开Word文档 doc = Document('example.docx') # 遍历文档中的所有段落 for para in doc.paragraphs: # 查找文本位置 if 'word' in para.text: print(f"Found 'word' in paragraph '{para.text}' at position {para.text.find('word')}.") # 遍历段落中的所有runs for run in para.runs: # 查找字体大小 if run.font.size is not None: print(f"Font size of run '{run.text}' is {run.font.size} points.") # 查找字体名称 if run.font.name is not None: print(f"Font name of run '{run.text}' is '{run.font.name}'.") # 查找字体颜色 if run.font.color is not None: print(f"Font color of run '{run.text}' is '{run.font.color.rgb}'.") ``` 在这个例子中,我们首先使用`Document`类打开Word文档,然后遍历文档中的所有段落。对于每个段落,我们使用`find()`方法来查找文本位置。对于每个段落中的所有runs,我们分别查找字体大小、字体名称和字体颜色。注意,`font.color.rgb`返回的是一个RGB元组,你可以将其转换为一个字符串,以便更方便地打印。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值