用来解析类如下面代码里 html 标签样式.
<span style="color: rgb(64, 169, 255);">文字内容</span>
<span style="color: rgb(64, 169, 255); font-size: 16px;"><strong>文字</strong></span>
使用:
val str = htmlStr.replace("span", CustomTagHandler.HANDLE_TAG)tvHtml.text =HtmlCompat.fromHtml(str,HtmlCompat.FROM_HTML_MODE_COMPACT,null,CustomTagHandler())
CustomTagHandler.kt 文件:
import android.graphics.Color
import android.text.Editable
import android.text.Html.TagHandler
import android.text.Spanned
import android.text.style.AbsoluteSizeSpan
import android.text.style.ForegroundColorSpan
import org.xml.sax.XMLReader/*** 自定义解析 html tag.** 解析字符串样式:* * val str =* """<p>* <span style="color: rgb(64, 169, 255);">文字内容</span>* </p>* <p>* <span style="color: rgb(64, 169, 255); font-size: 16px;"><strong>文字</strong></span>* </p>* """** @Author: qq.yang* @Date: 2024/2/26 14:33*/
internal class CustomTagHandler : TagHandler {companion object {const val HANDLE_TAG = "qqTag"}private var startIndex = 0private var stopIndex = 0private val attributes = HashMap<String, String>()override fun handleTag(opening: Boolean, tag: String, output: Editable, xmlReader: XMLReader?) {Logs.i("tagHandler -----> opening: $opening, tag: $tag, output: $output, xmlReader: ${xmlReader?.javaClass}")processAttributes(xmlReader)if (tag.equals(HANDLE_TAG, ignoreCase = true)) {if (opening) {startSpan(tag, output, xmlReader)} else {endSpan(tag, output, xmlReader)attributes.clear()}}}private fun startSpan(tag: String?, output: Editable, xmlReader: XMLReader?) {startIndex = output.length}private fun endSpan(tag: String?, output: Editable, xmlReader: XMLReader?) {stopIndex = output.lengthval style = attributes["style"]if (!style.isNullOrBlank()) {analysisStyle(startIndex, stopIndex, output, style)}}private fun processAttributes(xmlReader: XMLReader?) {xmlReader ?: returntry {val elementField = xmlReader.javaClass.getDeclaredField("theNewElement")elementField.isAccessible = trueval element = elementField[xmlReader]val attsField = element.javaClass.getDeclaredField("theAtts")attsField.isAccessible = trueval atts = attsField[element]val dataField = atts.javaClass.getDeclaredField("data")dataField.isAccessible = trueval data = dataField[atts] as Array<String>val lengthField = atts.javaClass.getDeclaredField("length")lengthField.isAccessible = trueval len = lengthField[atts] as Intfor (i in 0 until len) attributes[data[i * 5 + 1]] = data[i * 5 + 4]} catch (e: Exception) {// ignore.}}/*** 解析style属性*/private fun analysisStyle(startIndex: Int, stopIndex: Int, editable: Editable, style: String?) {Logs.i("tagHandler -----> analysisStyle -----> style:$style")style ?: returnval attrArray = style.split(";".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()val attrMap: MutableMap<String, String> = HashMap()for (attr in attrArray) {val keyValueArray = attr.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()if (keyValueArray.size == 2) {// 记住要去除前后空格attrMap[keyValueArray[0].trim { it <= ' ' }] =keyValueArray[1].trim { it <= ' ' }}}Logs.i("tagHandler -----> analysisStyle -----> attrMap:$attrMap")val fontSizeAttr = attrMap["font-size"]val fontSize = fontSizeAttr?.removeSuffix("px")?.toFloatOrNull()?.dpfontSize?.let {editable.setSpan(AbsoluteSizeSpan(fontSize.toInt()),startIndex,stopIndex,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)}val colorAttr = attrMap["color"]val fontColor = colorAttr?.let { parseColor(it) }fontColor?.let {editable.setSpan(ForegroundColorSpan(fontColor),startIndex,stopIndex,Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)}}// rgb(64, 169, 255) -> Colorprivate fun parseColor(rgbString: String): Int {Logs.i("tagHandler -----> parseColor -----> rgbString:$rgbString")// 移除字符串中的 "rgb(" 和 ")"val cleanedString = rgbString.replace("rgb(", "").replace(")", "")// 拆分字符串以获取红、绿、蓝分量值val components = cleanedString.split(", ").map { it.toInt() }val (red, green, blue) = components// 构建颜色值并返回return Color.rgb(red, green, blue)}
}