27、Android动画:补间动画、帧动画、属性动画
动画这东西,说白了就是让界面“活”起来。我刚开始做Android开发那会儿,觉得动画就是个锦上添花的东西。后来在做一个电商项目时,发现商品列表的加载动画做得流畅与否,直接影响了用户的停留时长。嗯,从那以后我再也不敢小看动画了。
Android的动画体系,主要分三大类:补间动画(Tween Animation)、帧动画(Frame Animation)和属性动画(Property Animation)。今天咱们就把它们挨个捋一遍。
补间动画:老牌选手,但有限制
补间动画是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);
补间动画适合简单的过渡效果,比如页面切换、图标闪烁。但如果你需要交互跟随,就别用它了。
帧动画:像放幻灯片一样
帧动画的原理很简单——把一张张图片按顺序播放,就像老式电影胶片。它通常用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。点击事件跟着走,不会出现“点不到”的情况。
核心类就两个:ValueAnimator和ObjectAnimator。
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开发的标配。我个人建议:新项目直接上属性动画,省心又强大。