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添加TranslateAnimation和AlphaAnimation组合动画,但效果远不如分开绘制灵活。
内容的提问来源于stack exchange,提问作者Mehedi Hasan




