Android:如何将 HTML 字符串转换为 Spanned 以在 TextView 中显示(必须适用于 API < 24)

Ada*_*Lee 5 string algorithm android kotlin

对于我的应用程序,我需要将包含 span(带有背景颜色)的 HTML 显示为 a Spanned,以便它可以显示在 a 上TextView,因为 AndroidTextView不支持该span标记。我首先尝试将 转换String为 a SpannableStringBuilder,然后从转换的字符串 ( Spanned) 中检索 HTML 编码的字符串。我需要它在 API 22-23 上工作,因此,我不能简单地使用 fromHTML,因为fromHTML它不支持span低于 24 的 API。我正在编写一个名为的函数fromHTML来完成此操作:

输入示例fromHTML(输入可以是任何带跨度的字符串):

Not highlighted string<span style=\"background-color: #FF8983\">Highlighted string</span> 
not highlighted string <span style=\"background-color: #FF8983\">Highlighted string</span> 
Run Code Online (Sandbox Code Playgroud)

下面是我的代码:

private fun fromHtml(source:String):Spanned {
    var htmlText:SpannableStringBuilder = source as SpannableStringBuilder;
    var htmlEncodedString:String = Html.toHtml(htmlText);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
    {
        return Html.fromHtml(htmlEncodedString, Html.FROM_HTML_MODE_LEGACY)
    }
    else
    {
        return Html.fromHtml(htmlEncodedString)
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,我收到以下错误:

java.lang.ClassCastException:java.lang.String无法转换为android.text.SpannableStringBuilder

如何将 HTML 字符串转换为 Spanned 对象以在 TextView 上显示(我需要此程序在 API 22-23 上工作,而在 API 22-23 上,不支持 span,因此我不能只使用简单的 fromHTML 转换

Che*_*amp 6

您收到类转换异常,因为您尝试将String转换为SpannableStringBuilder

var htmlText:SpannableStringBuilder = source as SpannableStringBuilder
Run Code Online (Sandbox Code Playgroud)

将字符串放入SpannableStringBuilder 的方法是

var htmlText = SpannableStringBuilder(source)
Run Code Online (Sandbox Code Playgroud)

关于对低于 24 的 API 的 HTML Span 标记支持,我认为HtmlCompat会为 API 24 功能提供 Span 标记支持,但事实并非如此。这意味着我们必须自己处理 span 标签。(由于 API 24 以下不支持 span 标签,我们可能会考虑使用 HTML 标签处理程序。不幸的是,标签处理程序会收到 Span 标签存在的警报,但 Span 属性不可用于标签处理程序:- (,所以我们必须执行以下操作。)

下面的示例代码将处理API 18+ 的spanbackground-color标签的属性。(它也可能支持低于 18 的 API。)该方法是使用正则表达式从 HTML 中提取适当的 span 属性值和文本,并将它们转换为跨度字符串中的 Android 文本跨度。然后可以将跨度字符串设置为TextView。您可以在代码中找到解释性注释。

以下是运行 API 18 的模拟器上的屏幕外观。突出显示是由下面的代码创建的,而粗体文本是通过调用Html.fromHtml()创建的。

在此输入图像描述

MainActivity.kt

class MainActivity : AppCompatActivity() {
    // Html.fromHTML() seems to be a little picky about how HTML attributes are delimited.
    // Mind the spaces!
    private val mHtmlString =
        "   <span style=\"color: #FFFFFF ; background-color: #FF8983\">Highlighted</span><b> Bold!</b> Not bold <span style=\"background-color: #00FF00\">string</span>"

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val tv = findViewById<TextView>(R.id.textView)
        tv.text = processHtml(mHtmlString)
    }

    private fun processHtml(s: String): Spanned? {

        // Easy for API 24+.
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            return HtmlCompat.fromHtml(s, HtmlCompat.FROM_HTML_MODE_LEGACY)
        }

        // HtmlCompat.fromHtml() for API 24+ can handle more <span> attributes than we try to here.
        // We will just process the background-color attribute.

        // HtmlCompat.fromHtml() will remove the spans in our string. Escape them before processing.
        var escapedSpans = s.replace("<span ", "&lt;span ", true)
        escapedSpans = escapedSpans.replace("</span>", "&lt;/span&gt;", true)

        // Process all the non-span tags the are supported pre-API 24.
        val spanned = HtmlCompat.fromHtml(escapedSpans, HtmlCompat.FROM_HTML_MODE_LEGACY)

        // Process HTML spans. Identify each background-color attribute and replace the effected
        // text with a BackgroundColorSpan. Here we assume that the background color is a hex number
        // starting with "#". Other value such as named colors can be handled with additional
        // processing.
        val sb = SpannableStringBuilder(spanned)
        val m: Matcher = SPAN_PATTERN.matcher(sb)
        do {
            if (m.find()) {
                val regionEnd = m.start(0) + m.group(2).length
                sb.replace(m.start(0), m.end(0), m.group(2))
                    .setSpan(
                        BackgroundColorSpan(parseInt(m.group(1), 16) or HTML_COLOR_OPAQUE_MASK),
                        m.start(0),
                        regionEnd,
                        Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
                    )
                m.reset(sb)
                m.region(regionEnd, sb.length)
            }
        } while (!m.hitEnd())
        return sb
    }

    companion object {
        val SPAN_PATTERN: Pattern =
            Pattern.compile("<span.*?background(?:-color)?:\\s*?#([^,]*?)[\\s;\"].*?>(.*?)</span>")
        const val HTML_COLOR_OPAQUE_MASK = 0xFF000000.toInt()
    }
}
Run Code Online (Sandbox Code Playgroud)