安卓中设置渐变字体和描边字体

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()
相关推荐
fatiaozhang95278 分钟前
中国移动中兴云电脑W132D-RK3528-2+32G-刷机固件包(非原机制作)
android·xml·电脑·电视盒子·刷机固件·机顶盒刷机
Android出海2 小时前
Google Play账户与App突遭封禁?紧急应对与快速重构上架策略
android·网络·重构·新媒体运营·产品运营·内容运营
恋猫de小郭2 小时前
Flutter 官方 LLM 动态 UI 库 flutter_genui 发布,让 App UI 自己生成 UI
android·前端·flutter
锅拌饭3 小时前
saveEnabled导致的Fragment大量泄露
android
叽哥3 小时前
Kotlin学习第 3 课:Kotlin 流程控制:掌握逻辑分支与循环的艺术
android·java·kotlin
CYRUS_STUDIO3 小时前
别让 so 裸奔!移植 OLLVM 到 NDK 并集成到 Android Studio
android·android studio·llvm
尚久龙3 小时前
安卓学习 之 图片控件和图片按钮
android·java·学习·手机·android studio·安卓
东风西巷3 小时前
Don‘t Sleep:保持电脑唤醒,确保任务不间断
android·电脑·软件需求
tangweiguo030519874 小时前
FlutterActivity vs FlutterFragmentActivity:全面对比与最佳实践
android·flutter
葱段4 小时前
【Flutter】TextField 监听长按菜单粘贴点击事件
android·flutter·dart