安卓使用Span富文本给某段Text文本加上波浪线

前言

最近项目需要给某段文字动态的加上波浪线,但是没搜到什么好的方案,于是打算自己实现一下,效果如下:

正文

本文使用的方案是自定义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

猜你喜欢

转载自blog.csdn.net/qq_33505109/article/details/123899399