You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Android中如何为canvas.drawText()实现淡入淡出、左滑显示等动画?

嘿,这个问题我之前帮不少开发者捋清楚过——如果你已经把文本直接画进Bitmap里了,那可得先纠正一个误区:此时文本已经是Bitmap的像素一部分,没法单独对它做动画。要实现文本的淡入+从左到右滑动效果,正确的思路是保留无文本的原始Bitmap,然后通过动态绘制文本的方式来实现,这样性能也更优。下面给你一步步的实现方案:

实现方案:自定义View + ValueAnimator 动态绘制

核心是用ValueAnimator控制文本的位置和透明度参数,在自定义View的onDraw方法里实时绘制文本状态,避免反复修改Bitmap带来的性能损耗。

步骤1:编写自定义AnimatedBitmapTextView

这个View会持有原始Bitmap和要显示的文本,通过动画参数动态控制文本的显示状态:

import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Paint
import android.util.AttributeSet
import android.view.View
import android.animation.ValueAnimator
import android.animation.AnimatorSet

class AnimatedBitmapTextView @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {

    private var originalBitmap: Bitmap? = null
    private var textToShow: String = ""
    private val textPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
        textSize = 60f
        color = 0xFF000000.toInt()
    }

    // 动画控制参数:文本X偏移量、透明度
    private var textOffsetX: Float = 0f
    private var textAlpha: Int = 0

    // 文本最终停留的目标位置(可根据需求调整)
    private var targetTextX: Float = 0f
    private var targetTextY: Float = 0f

    fun setBitmapAndText(bitmap: Bitmap, text: String) {
        originalBitmap = bitmap
        textToShow = text
        // 计算文本居中显示的目标位置(Bitmap下方)
        targetTextX = (bitmap.width - textPaint.measureText(text)) / 2f
        targetTextY = bitmap.height + textPaint.textSize + 20f // 预留20dp间距
        requestLayout()
        startAnimation()
    }

    private fun startAnimation() {
        // 位移动画:从屏幕左侧外(负文本宽度)滑到目标X位置
        val offsetAnimator = ValueAnimator.ofFloat(-textPaint.measureText(textToShow), targetTextX).apply {
            duration = 1500 // 1.5秒完成滑动
            addUpdateListener { anim ->
                textOffsetX = anim.animatedValue as Float
                invalidate() // 触发重绘
            }
        }

        // 淡入动画:透明度从0到255
        val alphaAnimator = ValueAnimator.ofInt(0, 255).apply {
            duration = 1200 // 1.2秒完成淡入
            addUpdateListener { anim ->
                textAlpha = anim.animatedValue as Int
                textPaint.alpha = textAlpha
                invalidate()
            }
        }

        // 同时启动两个动画
        AnimatorSet().apply {
            playTogether(offsetAnimator, alphaAnimator)
            start()
        }
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        originalBitmap?.let {
            // View尺寸设为Bitmap高度 + 文本高度 + 额外间距
            val desiredWidth = it.width
            val desiredHeight = (it.height + textPaint.textSize + 40).toInt()
            setMeasuredDimension(
                resolveSize(desiredWidth, widthMeasureSpec),
                resolveSize(desiredHeight, heightMeasureSpec)
            )
        } ?: super.onMeasure(widthMeasureSpec, heightMeasureSpec)
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        originalBitmap?.let { bitmap ->
            // 先绘制原始无文本Bitmap
            canvas.drawBitmap(bitmap, 0f, 0f, null)
            // 再绘制动态文本
            canvas.drawText(textToShow, textOffsetX, targetTextY, textPaint)
        }
    }
}

步骤2:在页面中使用自定义View

首先在布局文件中添加这个View:

<com.yourpackage.AnimatedBitmapTextView
    android:id="@+id/animatedView"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />

然后在Activity/Fragment中传入Bitmap和文本:

// 替换成你的无文本原始Bitmap
val originalBitmap = ... 
val animatedView = findViewById<AnimatedBitmapTextView>(R.id.animatedView)
animatedView.setBitmapAndText(originalBitmap, "你要展示的动画文本")

特殊情况说明

如果因为某些限制必须使用已带文本的Bitmap,那只能对整个Bitmap做动画(比如整体滑动+淡入),无法单独控制文本。这种情况下可以用ImageView包裹Bitmap,给ImageView添加TranslateAnimationAlphaAnimation组合动画,但效果远不如分开绘制灵活。

内容的提问来源于stack exchange,提问作者Mehedi Hasan

火山引擎 最新活动