27、Android动画:补间动画、帧动画、属性动画

动画这东西,说白了就是让界面“活”起来。我刚开始做Android开发那会儿,觉得动画就是个锦上添花的东西。后来在做一个电商项目时,发现商品列表的加载动画做得流畅与否,直接影响了用户的停留时长。嗯,从那以后我再也不敢小看动画了。

Android的动画体系,主要分三大类:补间动画(Tween Animation)帧动画(Frame Animation)属性动画(Property Animation)。今天咱们就把它们挨个捋一遍。

Android 动画体系 补间动画 帧动画 属性动画 Alpha / Scale / Translate / Rotate 逐帧播放图片序列 任意属性值变化 只改变绘制效果 不改变实际位置/大小 适合复杂序列帧 内存占用较大 真正改变属性值 灵活度最高 推荐优先使用属性动画(Android 3.0+)

补间动画:老牌选手,但有限制

补间动画是Android最早支持的动画方式。你只需要定义动画的起始和结束状态,中间的过程系统帮你算好。它支持四种基本变换:透明度(Alpha)缩放(Scale)平移(Translate)旋转(Rotate)

我个人习惯把补间动画定义在XML里,这样复用起来方便。举个例子:

<!-- res/anim/fade_in.xml -->
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromAlpha="0.0"
    android:toAlpha="1.0"
    android:duration="1000" />

然后在代码里加载:

Animation fadeIn = AnimationUtils.loadAnimation(this, R.anim.fade_in);
imageView.startAnimation(fadeIn);
⚠️ 注意:补间动画有个大坑——它只改变View的绘制效果,不改变View的实际位置。我在项目中遇到过,一个按钮做了平移动画后,点击事件还在原来的位置。用户明明看到按钮在右边,点下去却没反应。这体验太糟糕了。

补间动画适合简单的过渡效果,比如页面切换、图标闪烁。但如果你需要交互跟随,就别用它了。

帧动画:像放幻灯片一样

帧动画的原理很简单——把一张张图片按顺序播放,就像老式电影胶片。它通常用AnimationDrawable来实现。

先定义一个XML:

<!-- res/drawable/frame_anim.xml -->
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="false">
    <item android:drawable="@drawable/frame_1" android:duration="100" />
    <item android:drawable="@drawable/frame_2" android:duration="100" />
    <item android:drawable="@drawable/frame_3" android:duration="100" />
    <item android:drawable="@drawable/frame_4" android:duration="100" />
</animation-list>

然后设置给ImageView并启动:

ImageView iv = findViewById(R.id.iv_loading);
iv.setImageResource(R.drawable.frame_anim);
AnimationDrawable anim = (AnimationDrawable) iv.getDrawable();
anim.start();

核心要点:帧动画的每一帧都是一张完整的图片。如果你有30帧,每帧100KB,那就要占用3MB内存。我曾经在低端机上跑帧动画,直接OOM了。所以图片要压缩,帧数要控制。

💡 我的建议:帧动画的帧数不要超过20帧,单帧大小控制在50KB以内。如果动画复杂,考虑用属性动画或者Lottie替代。

属性动画:真正的动画王者

属性动画是Android 3.0(API 11)引入的。它跟补间动画最大的区别是:它真的去改对象的属性值。你让一个按钮从x=0移到x=100,它的实际坐标就真的变成了100。点击事件跟着走,不会出现“点不到”的情况。

核心类就两个:ValueAnimatorObjectAnimator

ValueAnimator 只负责计算值的变化,不直接操作对象。适合自定义动画逻辑:

ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
anim.setDuration(1000);
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        float value = (float) animation.getAnimatedValue();
        // 自己处理这个值,比如设置透明度、缩放等
        view.setAlpha(value);
    }
});
anim.start();

ObjectAnimator 更直接,你指定对象和属性名,它自动帮你赋值:

ObjectAnimator.ofFloat(view, "translationX", 0f, 300f)
    .setDuration(1000)
    .start();

你想想看,这比补间动画方便多了吧?而且它支持任意属性,只要该属性有setXxx()方法就行。

属性动画的高级用法

属性动画还支持动画集(AnimatorSet),可以组合多个动画:

ObjectAnimator moveX = ObjectAnimator.ofFloat(view, "translationX", 0f, 200f);
ObjectAnimator moveY = ObjectAnimator.ofFloat(view, "translationY", 0f, 200f);
ObjectAnimator fadeOut = ObjectAnimator.ofFloat(view, "alpha", 1f, 0f);

AnimatorSet set = new AnimatorSet();
set.play(moveX).with(moveY);  // 同时移动X和Y
set.play(fadeOut).after(moveX); // 移动结束后淡出
set.setDuration(800);
set.start();

关键点:属性动画默认是线性插值,你可以通过setInterpolator()改变节奏。比如用AccelerateDecelerateInterpolator让动画先加速后减速,看起来更自然。

三种动画的对比

对比维度 补间动画 帧动画 属性动画
引入版本 API 1 API 1 API 11
操作对象 View Drawable 任意对象
是否改变实际属性
内存占用
灵活度 低(仅4种变换) 中(依赖图片) 高(任意属性)
推荐场景 简单过渡效果 复杂序列帧 交互式动画

⚠️ 避坑指南:我曾经在RecyclerView的item里用属性动画做点赞效果,结果快速滑动时卡成PPT。后来发现是动画对象没有及时取消。记住:页面退出或View被回收时,一定要调用animator.cancel(),否则会内存泄漏。

性能优化小贴士

动画做得好不好,直接看帧率。我一般用GPU呈现模式分析来检测。如果动画掉帧,优先检查以下几点:

  • 减少过度绘制:动画过程中避免复杂的背景叠加
  • 使用硬件加速:在AndroidManifest或View级别开启
  • 避免在动画中触发布局:比如不要边动画边调用requestLayout()
  • 属性动画优先用translationX/Y:比修改left/top性能更好

💡 我的习惯:对于频繁触发的动画(比如列表滑动时的视差效果),我会用ViewPropertyAnimator。它是对属性动画的封装,写法更简洁,性能也更好:view.animate().translationX(100f).setDuration(300).start();

好了,三种动画的核心内容就这些。补间动画适合老项目兼容,帧动画适合简单循环效果,属性动画则是现代Android开发的标配。我个人建议:新项目直接上属性动画,省心又强大。

公众号:蓝海资料掘金营,微信 deep3321