Android uses Span rich text to add a wavy line to a text

foreword

Recently, the project needs to dynamically add wavy lines to a certain text, but I can't find any good solutions, so I plan to implement it myself. The effect is as follows:

text

The solution used in this article is to customize Span rich text, and use Bezier curves to draw wavy lines in Span

code show as below:


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
}

The code has comments, the logic is very simple, that is, define your own Span, calculate the width of the wavy line to be drawn, and use the Bezier curve api to draw Bezier curves in the corresponding width and high school

Code used:

tv.text = "1234567890".toWaveLineSpan(3, 7)

The effect is as follows:

end

Guess you like

Origin blog.csdn.net/qq_33505109/article/details/123899399