1.CommonFontSpan
abstract class CommonFontSpan : ReplacementSpan() {
/** 测量的文本宽度 */
private var mMeasureTextWidth = 0f
override fun getSize(
paint: Paint,
text: CharSequence?,
start: Int,
end: Int,
fontMetricsInt: FontMetricsInt?
): Int {
mMeasureTextWidth = onMeasure(paint, fontMetricsInt, text, start, end)
// 这段不可以去掉,字体高度没设置,会出现 draw 方法没有被调用的问题
val metrics = paint.fontMetricsInt
if (fontMetricsInt != null) {
fontMetricsInt.top = metrics.top
fontMetricsInt.ascent = metrics.ascent
fontMetricsInt.descent = metrics.descent
fontMetricsInt.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 = 0f
private 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 = linearGradient
canvas.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 = true
mStrokePaint.textSize = paint.textSize
// 描边宽度
mStrokePaint.strokeWidth = if (isSp) mTextStrokeSize.dp else mTextStrokeSize.toFloat()
mStrokePaint.style = Paint.Style.STROKE
// 设置粗体
paint.isFakeBoldText = paint.typeface === Typeface.DEFAULT_BOLD
mStrokePaint.color = mTextStrokeColor
canvas.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()