You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android TextView使用fromHtml()如何支持原生不支持的font-size等CSS样式

解决方案

核心思路是通过HtmlCompat.fromHtml提供的自定义TagHandler扩展能力,在不修改原生解析逻辑的基础上,额外解析标签style属性中的font-size配置,添加对应的字号Span,完全符合要求。

实现步骤

  • 自定义FontSizeTagHandler类实现Html.TagHandler接口,内部维护栈存储标签起始位置和提取到的字号值,仅对需要补充font-size支持的标签做额外处理,不覆盖原生标签解析逻辑
  • 调用HtmlCompat.fromHtml时传入自定义的TagHandler实例即可

完整代码示例

自定义TagHandler实现

import android.text.Editable;
import android.text.Spannable;
import android.text.style.AbsoluteSizeSpan;
import android.util.Log;
import android.util.TypedValue;
import android.content.res.Resources;
import org.xml.sax.XMLReader;
import java.util.Stack;
import androidx.core.text.HtmlCompat;

public class FontSizeTagHandler implements HtmlCompat.TagHandler {
    // 存储标签信息的实体类
    private static class FontSizeTag {
        int startPos;
        int fontSizePx;

        FontSizeTag(int startPos, int fontSizePx) {
            this.startPos = startPos;
            this.fontSizePx = fontSizePx;
        }
    }

    private final Stack<FontSizeTag> tagStack = new Stack<>();

    @Override
    public void handleTag(boolean opening, String tag, Editable output, XMLReader xmlReader) {
        // 只处理p标签,可根据需求扩展支持span、div等其他标签
        if (!tag.equalsIgnoreCase("p")) return;

        if (opening) {
            // 标签开始时,提取style属性中的font-size
            String style = getAttributeValue(xmlReader, "style");
            if (style != null && !style.isEmpty()) {
                int fontSizePx = parseFontSizeFromStyle(style);
                if (fontSizePx > 0) {
                    // 记录当前文本起始位置和字号,压入栈
                    tagStack.push(new FontSizeTag(output.length(), fontSizePx));
                }
            }
        } else {
            // 标签结束时,取出栈中记录的信息,设置字号Span
            if (!tagStack.isEmpty()) {
                FontSizeTag tagInfo = tagStack.pop();
                int endPos = output.length();
                if (tagInfo.startPos < endPos) {
                    output.setSpan(
                        new AbsoluteSizeSpan(tagInfo.fontSizePx),
                        tagInfo.startPos,
                        endPos,
                        Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
                    );
                }
            }
        }
    }

    // 从XMLReader中获取指定属性值
    private String getAttributeValue(XMLReader xmlReader, String attrName) {
        try {
            Object element = xmlReader.getProperty("http://xml.org/sax/properties/element-stack");
            if (element instanceof Stack) {
                Stack<?> elementStack = (Stack<?>) element;
                if (!elementStack.isEmpty()) {
                    Object node = elementStack.peek();
                    java.lang.reflect.Field attrsField = node.getClass().getField("attrs");
                    Object attrs = attrsField.get(node);
                    if (attrs instanceof org.xmlpull.v1.XmlPullParser) {
                        org.xmlpull.v1.XmlPullParser parser = (org.xmlpull.v1.XmlPullParser) attrs;
                        for (int i = 0; i < parser.getAttributeCount(); i++) {
                            if (parser.getAttributeName(i).equalsIgnoreCase(attrName)) {
                                return parser.getAttributeValue(i);
                            }
                        }
                    }
                }
            }
        } catch (Exception e) {
            Log.e("FontSizeTagHandler", "获取属性失败", e);
        }
        return null;
    }

    // 从style字符串中解析font-size,仅用字符串拆分,不使用正则
    private int parseFontSizeFromStyle(String style) {
        // 按分号拆分每个样式项
        String[] styleItems = style.split(";");
        for (String item : styleItems) {
            item = item.trim();
            // 按冒号拆分键值对
            String[] keyValue = item.split(":");
            if (keyValue.length == 2) {
                String key = keyValue[0].trim();
                String value = keyValue[1].trim();
                if (key.equalsIgnoreCase("font-size")) {
                    // 示例处理px单位,可扩展支持sp、dp等单位转换
                    if (value.endsWith("px")) {
                        try {
                            return Integer.parseInt(value.replace("px", "").trim());
                        } catch (NumberFormatException e) {
                            return 0;
                        }
                    }
                    // 如需支持dp,可打开以下注释
                    // if (value.endsWith("dp")) {
                    //     int dpVal = Integer.parseInt(value.replace("dp", "").trim());
                    //     return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpVal, Resources.getSystem().getDisplayMetrics());
                    // }
                }
            }
        }
        return 0;
    }
}

调用方式

// 你的HTML内容
String html = "<p style=\"font-size:30px\">TEST</p>";
// 调用fromHtml传入自定义TagHandler
CharSequence styledText = HtmlCompat.fromHtml(
    html,
    HtmlCompat.FROM_HTML_MODE_LEGACY,
    null, // 不需要处理图片传null即可
    new FontSizeTagHandler()
);
// 设置到TextView
textView.setText(styledText);

方案说明

  • 原生支持的所有样式(字体颜色、粗体、斜体等)都会完全保留,仅额外添加字号Span,没有覆盖原有解析逻辑
  • 没有使用正则表达式或者第三方HTML解析库,所有扩展逻辑都基于Android原生提供的API实现,不会出现解析逻辑差异问题
  • 不需要使用WebView,性能和原生TextView完全一致
  • 可扩展支持其他标签(span、div等),仅需修改handleTag中判断标签名的逻辑即可,也可扩展支持sp、dp等其他字号单位

内容的提问来源于stack exchange,提问作者kkkkk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 03:48:03