@korbonix 发布的答案效果很好。我在 Kotlin 中做了一些改进并支持多行 TextView:
class ColorUnderlineSpan(val underlineColor: Int, val underlineStart: Int, val underlineEnd: Int): LineBackgroundSpan {
val paint = Paint()
init {
paint.color = underlineColor
paint.strokeWidth = 3.0f
paint.style = Paint.Style.FILL_AND_STROKE
}
override fun drawBackground(c: Canvas?, p: Paint?, left: Int, right: Int, top: Int, baseline: Int, bottom: Int, text: CharSequence?, start: Int, end: Int, lnum: Int) {
if (!(underlineStart < underlineEnd)) {
throw Error("underlineEnd should be greater than underlineStart")
}
if (underlineStart > end || underlineEnd < start) {
return
}
var offsetX = 0
if (underlineStart > start) {
offsetX = p?.measureText(text?.subSequence(start, underlineStart).toString())?.toInt() ?: 0
}
val length: Int = p?.measureText(text?.subSequence(Math.max(start, underlineStart), Math.min(end, underlineEnd)).toString())?.toInt()
?: 0
c?.drawLine(offsetX.toFloat(), baseline + 3.0f, (length + offsetX).toFloat(), baseline + 3.0f, paint)
}
}
这是一个示例用法。textText 是文本视图。文本长度为 127 个字符,下划线从位置 112 到 127。
重要提示:由于我不完全理解的原因,跨度长度应设置为文本的全长。否则该组件甚至不会被调用。随时教育我为什么会这样。
// Sets link color
val spannable = SpannableString(getString(R.string.forgot_text))
spannable.setSpan(
ColorUnderlineSpan(Color.RED), 112, 127),
0, 127, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
textText.text = spannable