I know how to underline text in a textview. But how to underline text with some different color ? Underline can be done with:
TextView t = (TextView) findViewById(R.id.textview);
t.setPaintFlags(t.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
t.setText("Underline Text");
Let say my textcolor is black and I want to underline the same with blue color, how to do it ? Thanks in advance.
I had the same problem and I have stumbled upon Layout class when reading some other posts for doing this on EditText. It provides everything you need to make this happen by manually drawing underline with canvas.
First I defined custom attributes for an easy customization in XML layout files
<declare-styleable name="UnderlinedTextView" >
<attr name="underlineHeight" format="dimension" />
<attr name="underlineOffset" format="dimension" />
<attr name="underlineColor" format="color" />
<attr name="underLinePosition" format="enum">
<enum name="baseline" value="0" />
<enum name="below" value="1" />
</attr>
</declare-styleable>
And a custom TextView class
class UnderlinedTextView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : androidx.appcompat.widget.AppCompatTextView(context, attrs, defStyleAttr) {
@Retention(AnnotationRetention.SOURCE)
@IntDef(POSITION_BASELINE, POSITION_BELOW)
annotation class UnderLinePosition {
companion object {
const val POSITION_BASELINE = 0
const val POSITION_BELOW = 1
}
}
private val linePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
style = Paint.Style.FILL
}
var lineColor: Int
get() = linePaint.color
set(value) {
if (linePaint.color != value) {
linePaint.color = value
invalidate()
}
}
var lineHeight: Float
get() = linePaint.strokeWidth
set(value) {
if (linePaint.strokeWidth != value) {
linePaint.strokeWidth = value
updateSpacing()
}
}
var lineTopOffset = 0F
set(value) {
if (field != value) {
field = value
updateSpacing()
}
}
@UnderLinePosition
var linePosition = POSITION_BASELINE
private val rect = Rect()
private var internalAdd: Float = lineSpacingExtra
private inline val extraSpace
get() = lineTopOffset + lineHeight
init {
val density = context.resources.displayMetrics.density
val typedArray = context.obtainStyledAttributes(attrs, R.styleable.UnderlinedTextView, defStyleAttr, 0)
lineColor = typedArray.getColor(R.styleable.UnderlinedTextView_underlineColor, currentTextColor)
lineTopOffset = typedArray.getDimension(R.styleable.UnderlinedTextView_underlineOffset, 0f)
lineHeight = typedArray.getDimension(R.styleable.UnderlinedTextView_underlineHeight, density * 1)
linePosition = typedArray.getInt(R.styleable.UnderlinedTextView_underLinePosition, POSITION_BASELINE)
typedArray.recycle()
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
setMeasuredDimension(measuredWidth, measuredHeight + (extraSpace + 0.5f).toInt())
}
override fun onDraw(canvas: Canvas?) {
canvas?.takeIf { !text.isNullOrEmpty() }?.let {
val count = lineCount
val layout = layout
var xStart: Float
var xStop: Float
var yStart: Float
var firstCharInLine: Int
var lastCharInLine: Int
var lastLine: Boolean
var offset: Int
val lineSpacing = lineSpacingExtra * lineSpacingMultiplier
for (i in 0 until count) {
val baseline = getLineBounds(i, rect)
lastLine = i == count - 1
offset = if (lastLine) 0 else 1
firstCharInLine = layout.getLineStart(i)
lastCharInLine = layout.getLineEnd(i)
xStart = layout.getPrimaryHorizontal(firstCharInLine)
xStop = layout.getPrimaryHorizontal(lastCharInLine - offset)
yStart = when (linePosition) {
POSITION_BASELINE -> baseline + lineTopOffset
POSITION_BELOW -> (rect.bottom + lineTopOffset) - if (lastLine) 0F else lineSpacing
else -> throw NotImplementedError("")
}
canvas.drawRect(xStart, yStart, xStop, yStart + lineHeight, linePaint)
}
}
super.onDraw(canvas)
}
private fun updateSpacing() {
setLineSpacing(internalAdd, 1f)
}
override fun setLineSpacing(add: Float, mult: Float) {
internalAdd = add
super.setLineSpacing(add + extraSpace, 1f)
}
}
Then it's usage is simple
<some.package.UnderlinedTextView
android:id="@+id/tvTest"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginBottom="10dp"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"
android:gravity="center"
android:text="This is a demo text"
android:textSize="16sp"
app:underlineColor="#ffc112ef"
app:underlineHeight="3dp"/>
Final result
You can try out as below:
String styledText = "<u><font color='red'>Underline Text</font></u>."; textView.setText(Html.fromHtml(styledText), TextView.BufferType.SPANNABLE);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With