Another solution, this time without extending TextView (based on a question I wrote a long time ago, here) :
Have a drawable to be shown as the underline, and have a span for the text itself :
text_underline.xml
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="line">
<padding android:bottom="10dp"/>
<stroke
android:width="1dp"
android:color="#3792e5"/>
</shape>
DrawableSpan.kt
class DrawableSpan(private val drawable: Drawable) : ReplacementSpan() {
private val padding: Rect = Rect()
init {
drawable.getPadding(padding)
}
override fun draw(canvas: Canvas, text: CharSequence, start: Int, end: Int, x: Float, top: Int, y: Int, bottom: Int, paint: Paint) {
val rect = RectF(x, top.toFloat(), x + measureText(paint, text, start, end), bottom.toFloat())
drawable.setBounds(rect.left.toInt() - padding.left, rect.top.toInt() - padding.top, rect.right.toInt() + padding.right, rect.bottom.toInt() + padding.bottom)
canvas.drawText(text, start, end, x, y.toFloat(), paint)
drawable.draw(canvas)
}
override fun getSize(paint: Paint, text: CharSequence, start: Int, end: Int, fm: Paint.FontMetricsInt?): Int = Math.round(paint.measureText(text, start, end))
private fun measureText(paint: Paint, text: CharSequence, start: Int, end: Int): Float = paint.measureText(text, start, end)
}
usage:
val text = getString(R.string.large_text)
val spannable = SpannableString(text)
spannable.setSpan(DrawableSpan(resources.getDrawable(R.drawable.text_underline)), 0, text.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
textView.setText(spannable, TextView.BufferType.SPANNABLE)
And the result:
