使用 ReplacementSpan¶
ReplacementSpan是android.text.style
里的一个抽象类。
我们主要在draw
方法中对文本进行操作。
实现下划线效果¶
准备工作¶
新建一个drawable_tv_underline.xml
。用来充当下划线。
可以修改下划线的颜色和粗细。
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="line">
<stroke
android:width="2dp"
android:color="#3792e5" />
</shape>
以此类推,再建立一个drawable_tv_underline2.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="line">
<stroke
android:width="1dp"
android:color="#FF7700" />
</shape>
实现Span¶
新建DrawableSpan.kt
文件,DrawableSpan继承ReplacementSpan。
需要传入一个Drawable对象来当下划线。
在draw
方法中,我们先计算出文字的显示范围,以此来确定下划线的尺寸和位置。
import android.graphics.*
import android.graphics.drawable.Drawable
import android.text.style.ReplacementSpan
import kotlin.math.roundToInt
class DrawableSpan(private val drawable: Drawable) : ReplacementSpan() {
private val padding: Rect = Rect()
var textColor = Color.parseColor("#3792e5")
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() + rect.height() / 1.2f).toInt())
paint.color = textColor
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 = paint.measureText(text, start, end).roundToInt()
private fun measureText(paint: Paint, text: CharSequence, start: Int, end: Int): Float = paint.measureText(text, start, end)
}
使用¶
使用这个DrawableSpan。 模拟了一个使用场景。
final String text1 = "RustFisher:\nHow to underline text in TextView with some different color than that of text?";
SpannableStringBuilder ssb = new SpannableStringBuilder(text1);
for (int i = 0; i < text1.length(); i += 6) {
ssb.setSpan(new DrawableSpan(getResources().getDrawable(R.drawable.drawable_tv_underline)), i, Math.min(i + 4, text1.length()), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
mBinding.tv1.setText(ssb);
SpannableStringBuilder ssb2 = new SpannableStringBuilder(text1);
for (int i = 0; i < text1.length(); i += 6) {
ssb2.setSpan(new DrawableSpan(getResources().getDrawable(R.drawable.drawable_tv_underline2)), i, Math.min(i + 4, text1.length()), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
mBinding.tv2.setText(ssb2);
运行效果¶
可以看出,有下划线的部分,文字颜色也被修改了。
多个文字颜色与下划线¶
前面我们实现了下划线效果。从上面的效果图我们看到下划线和文字颜色都可以修改。 那么我们可以根据这个特点来增加文字变色效果。
如果只是修改文字颜色,我们也可以使用ForegroundColorSpan来实现。 这里我们实现的是从头开始的变色效果。
实现span类¶
draw
方法里,我们需要处理一下变色终点下标foregroundEndIndex
与文本结尾end
之间的关系。
import android.graphics.*
import android.graphics.drawable.Drawable
import android.text.style.ReplacementSpan
import kotlin.math.roundToInt
class UnderlineAndForegroundSpan(private val drawable: Drawable) : ReplacementSpan() {
private val padding: Rect = Rect()
var textColor = Color.parseColor("#202020")
var foregroundColor = Color.parseColor("#F8872E")
var foregroundEndIndex = 0
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() + rect.height() / 1.2f).toInt())
if (foregroundEndIndex <= 0) {
paint.color = textColor
canvas.drawText(text, start, end, x, y.toFloat(), paint)
} else {
when {
end <= foregroundEndIndex -> {
paint.color = foregroundColor
canvas.drawText(text, start, end, x, y.toFloat(), paint)
}
start < foregroundEndIndex -> {
// 把文字分开2段画
paint.color = foregroundColor
canvas.drawText(text, start, foregroundEndIndex, x, y.toFloat(), paint)
paint.color = textColor
val drawInPart = text.subSequence(start, foregroundEndIndex)
canvas.drawText(text, foregroundEndIndex, end, x + measureText(paint, drawInPart, 0, drawInPart.length), y.toFloat(), paint)
}
else -> {
paint.color = textColor
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 = paint.measureText(text, start, end).roundToInt()
private fun measureText(paint: Paint, text: CharSequence, start: Int, end: Int): Float = paint.measureText(text, start, end)
}
使用与运行效果¶
与前面类似,模拟一个使用场景。
单独使用¶
final String text = "RustFisher:\nHow to underline text in TextView with some different color than that of text?";
SpannableStringBuilder ssb = new SpannableStringBuilder(text);
for (int i = 0; i < text.length(); i += 6) {
UnderlineAndForegroundSpan span = new UnderlineAndForegroundSpan(getResources().getDrawable(R.drawable.drawable_tv_underline));
span.setForegroundEndIndex(text.length() / 2);
ssb.setSpan(span, i, Math.min(i + 4, text.length()), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
mBinding.tv3.setText(ssb);
结合ForegroundColorSpan¶
先使用ForegroundColorSpan来设定颜色,再把下划线效果加进去。
SpannableStringBuilder ssb2 = new SpannableStringBuilder(text);
final int foregroundColor = Color.RED;
ssb2.setSpan(new ForegroundColorSpan(foregroundColor), 0, text.length() / 2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
for (int i = 0; i < text.length(); i += 6) {
UnderlineAndForegroundSpan span = new UnderlineAndForegroundSpan(getResources().getDrawable(R.drawable.drawable_tv_underline));
span.setForegroundEndIndex(text.length() / 2);
span.setForegroundColor(foregroundColor);
ssb2.setSpan(span, i, Math.min(i + 4, text.length()), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
mBinding.tv4.setText(ssb2);
参考¶
- https://stackoverflow.com/questions/19046614/how-to-underline-text-in-textview-with-some-different-color-than-that-of-text
本站说明
一起在知识的海洋里呛水吧。广告内容与本站无关。如果喜欢本站内容,欢迎投喂作者,谢谢支持服务器。如有疑问和建议,欢迎在下方评论~