前言
最近项目需要给某段文字动态的加上波浪线,但是没搜到什么好的方案,于是打算自己实现一下,效果如下:
正文
本文使用的方案是自定义Span富文本,并在Span中用贝塞尔曲线来绘制出波浪线
代码如下:
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Path
import android.text.style.ReplacementSpan
import com.lt.androidkj.utils.extend.dpFloat
/**
* creator: lt 2022/3/31 [email protected]
* effect : 波浪线富文本
* [waveWidth]单个波浪线的宽,单位像素
* [waveHeight]单个波浪线的高,单位像素
* warning:
*/
class WaveLineSpan(
private val waveWidth: Float = 11.dpFloat(),
private val waveHeight: Float = 4.dpFloat()
) : ReplacementSpan() {
private val mStrokeWidth = 1.2.dpFloat()
private val mPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = Color.BLACK
style = Paint.Style.STROKE
strokeWidth = mStrokeWidth
}
private val mPath = Path()
private var width = 0f
//计算span的宽度
override fun getSize(
paint: Paint,
text: CharSequence?,
start: Int,
end: Int,
fm: Paint.FontMetricsInt?
): Int {
width = paint.measureText(text, start, end)
return width.toInt()
}
override fun draw(
canvas: Canvas,
text: CharSequence?,
start: Int,
end: Int,
x: Float,
top: Int,
y: Int,
bottom: Int,
paint: Paint
) {
//画出text
canvas.drawText(text.toString(), start, end, x, y.toFloat(), paint)
//四舍五入计算总个数
val number = Math.round(width / waveWidth)
//计算初始偏移,以使波浪线居中
var xOffset = (width - number * waveWidth) / 2
val y = y - mStrokeWidth
val waveHeight = waveHeight + y
//画出贝塞尔曲线
mPath.moveTo(x + xOffset, waveHeight)// 1.路径对象Path.moveTo(x,y);//指定曲线的起点
repeat(number) {
mPath.quadTo(
x + waveWidth / 2 + xOffset,
y,
x + waveWidth + xOffset,
waveHeight
)// 2.路径对象.quadTo(控制点x,y,曲线的终点x,y);//绘制贝塞尔曲线
xOffset += waveWidth
}
mPath.lineTo(
x + xOffset,
waveHeight
)// 3.路径对象.close();//path会默认闭合,使其闭合;lineTo不闭合
canvas.drawPath(mPath, mPaint) // 4.绘制:画布对象.drawPath(路径对象,画笔对象);
}
}
//dp转px,下面的app就是你的Application
fun Number.dpFloat(): Float =
this.toFloat() * app.resources.displayMetrics.density
/**
* 设置某一段文本具有底部波浪线
*/
fun CharSequence.toWaveLineSpan(start: Int, end: Int, waveSize: Float = 11.dpFloat()): SpannableString {
val s = if (this is SpannableString) this else SpannableString(this)
s.setSpan(
WaveLineSpan(waveSize),
start, end,
Spannable.SPAN_INCLUSIVE_EXCLUSIVE
)
return s
}
代码都有注释,逻辑很简单,就是定义一个自己的Span,计算要画的波浪线的宽度,并在相应的宽高中用贝塞尔曲线api来画一个个贝塞尔曲线
使用的代码:
tv.text = "1234567890".toWaveLineSpan(3, 7)
效果如下:
end