1.CommonFontSpan
abstract class CommonFontSpan : ReplacementSpan() {/** 测量的文本宽度 */private var mMeasureTextWidth = 0foverride fun getSize(paint: Paint,text: CharSequence?,start: Int,end: Int,fontMetricsInt: FontMetricsInt?): Int {mMeasureTextWidth = onMeasure(paint, fontMetricsInt, text, start, end)// 这段不可以去掉,字体高度没设置,会出现 draw 方法没有被调用的问题val metrics = paint.fontMetricsIntif (fontMetricsInt != null) {fontMetricsInt.top = metrics.topfontMetricsInt.ascent = metrics.ascentfontMetricsInt.descent = metrics.descentfontMetricsInt.bottom = metrics.bottom}return mMeasureTextWidth.toInt()}override fun draw(canvas: Canvas,text: CharSequence?,start: Int,end: Int,x: Float,top: Int,y: Int,bottom: Int,paint: Paint) {val alpha = paint.alpha// 判断是否给画笔设置了透明度if (alpha != 255) {// 如果是则设置不透明paint.alpha = 255}text?.let {onDraw(canvas, paint, it, start, end, x, top, y, bottom)}// 绘制完成之后将画笔的透明度还原回去paint.alpha = alpha}private fun onMeasure(paint: Paint,fontMetricsInt: FontMetricsInt?,text: CharSequence?,@IntRange(from = 0) start: Int,@IntRange(from = 0) end: Int): Float {return paint.measureText(text, start, end)}abstract fun onDraw(canvas: Canvas,paint: Paint,text: CharSequence,@IntRange(from = 0) start: Int,@IntRange(from = 0) end: Int,x: Float,top: Int,y: Int,bottom: Int)fun getMeasureTextWidth(): Float {return mMeasureTextWidth}
}
2.MultiFontSpan
class MultiFontSpan(vararg replacementSpans: ReplacementSpan) : ReplacementSpan() {/** 测量的文本宽度 */private var mMeasureTextWidth = 0fprivate var mReplacementSpans: List<ReplacementSpan> = mutableListOf()init {mReplacementSpans = replacementSpans.toList()}override fun getSize(paint: Paint,text: CharSequence?,start: Int,end: Int,fm: Paint.FontMetricsInt?): Int {for (replacementSpan in mReplacementSpans) {val size = replacementSpan.getSize(paint, text, start, end, fm)mMeasureTextWidth = Math.max(mMeasureTextWidth, size.toFloat())}return mMeasureTextWidth.toInt()}override fun draw(canvas: Canvas,text: CharSequence?,start: Int,end: Int,x: Float,top: Int,y: Int,bottom: Int,paint: Paint) {for (replacementSpan in mReplacementSpans) {replacementSpan.draw(canvas, text, start, end, x, top, y, bottom, paint)}}override fun updateDrawState(ds: TextPaint?) {super.updateDrawState(ds)for (replacementSpan in mReplacementSpans) {replacementSpan.updateDrawState(ds)}}override fun updateMeasureState(p: TextPaint) {super.updateMeasureState(p)for (replacementSpan in mReplacementSpans) {replacementSpan.updateMeasureState(p)}}
}
3.LinearGradientFontSpan
class LinearGradientFontSpan(private val mTextGradientColor: IntArray, /** 文字渐变颜色组 */private val mTextSize: Int? = null, /** 文字渐变字体大小(字体大小不一致的话这里必须动态设置) */private val mTextGradientOrientation: Int? = LinearLayout.VERTICAL, /** 文字渐变方向 */private val mTextGradientPositions: FloatArray? = null, /** 文字渐变位置组 */
) : CommonFontSpan() {override fun onDraw(canvas: Canvas,paint: Paint,text: CharSequence,start: Int,end: Int,x: Float,top: Int,y: Int,bottom: Int) {mTextSize?.let { paint.textSize = it.sp }val linearGradient = if (mTextGradientOrientation == LinearLayout.VERTICAL) {LinearGradient(0f, 0f, 0f, bottom.toFloat(), mTextGradientColor, mTextGradientPositions, Shader.TileMode.REPEAT)} else {LinearGradient(x, 0f, x + getMeasureTextWidth(), 0f, mTextGradientColor, mTextGradientPositions, Shader.TileMode.REPEAT)}paint.shader = linearGradientcanvas.drawText(text, start, end, x, y.toFloat(), paint)}override fun getSize(paint: Paint,text: CharSequence?,start: Int,end: Int,fontMetricsInt: Paint.FontMetricsInt?): Int {mTextSize?.let { paint.textSize = it.sp }return super.getSize(paint, text, start, end, fontMetricsInt)}
}
4.StrokeFontSpan
class StrokeFontSpan(private val mTextStrokeColor: Int,private val mTextStrokeSize: Int,private val isSp: Boolean = true,
) : CommonFontSpan() {/** 描边画笔 */private val mStrokePaint = Paint()override fun onDraw(canvas: Canvas,paint: Paint,text: CharSequence,start: Int,end: Int,x: Float,top: Int,y: Int,bottom: Int) {mStrokePaint.set(paint)// 设置抗锯齿mStrokePaint.isAntiAlias = true// 设置防抖动mStrokePaint.isDither = truemStrokePaint.textSize = paint.textSize// 描边宽度mStrokePaint.strokeWidth = if (isSp) mTextStrokeSize.dp else mTextStrokeSize.toFloat()mStrokePaint.style = Paint.Style.STROKE// 设置粗体paint.isFakeBoldText = paint.typeface === Typeface.DEFAULT_BOLDmStrokePaint.color = mTextStrokeColorcanvas.drawText(text, start, end, x, y.toFloat(), mStrokePaint)canvas.drawText(text, start, end, x, y.toFloat(), paint)}
}
5.使用
SpanUtils.with(mBindView.tvAnswerBtn1).append("豆奶").setSpans(StrokeFontSpan(Color.WHITE, 1)).append("好喝").setSpans(LinearGradientFontSpan(intArrayOf(Color.RED, Color.BLUE))).append("还要").setSpans(MultiFontSpan(StrokeFontSpan(Color.GREEN, 2),LinearGradientFontSpan(intArrayOf(Color.WHITE, Color.YELLOW)))).create()
设置渐变字体 大小不同
val colors = intArrayOf(Color.parseColor("#FDF4E9"), Color.parseColor("#FFDD63"))SpanUtils.with(mBindView.tvMaxMoney).append("30").setBold().setSpans(LinearGradientFontSpan(colors, 100)).append("元").setSpans(LinearGradientFontSpan(colors, 32)).create()