30、动画基础:补间动画与属性动画
动画这东西,说白了就是让界面“活”起来。我刚开始做Android开发那会儿,觉得动画就是个花架子,后来才发现——好的动画能让用户体验提升一个档次。今天咱们就聊聊Android里最基础也最常用的两种动画:补间动画和属性动画。
补间动画(Tween Animation)
补间动画,也叫View动画。它只改变View的绘制效果,不改变View的真实属性。什么意思呢?你想想看,一个按钮做了平移动画后,点击事件还在原来的位置——这就是补间动画的“坑”。
补间动画支持四种效果:
- Alpha — 透明度变化
- Scale — 缩放变化
- Translate — 平移变化
- Rotate — 旋转变化
它们可以单独使用,也可以组合使用。我习惯用XML定义动画,这样复用起来方便。
XML方式定义补间动画
在 res/anim/ 目录下创建XML文件:
<!-- fade_in.xml -->
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:fromAlpha="0.0"
android:toAlpha="1.0"
android:duration="1000" />
<!-- scale_rotate.xml -->
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="true"
android:duration="800">
<scale
android:fromXScale="0.5"
android:toXScale="1.5"
android:fromYScale="0.5"
android:toYScale="1.5"
android:pivotX="50%"
android:pivotY="50%" />
<rotate
android:fromDegrees="0"
android:toDegrees="360"
android:pivotX="50%"
android:pivotY="50%" />
</set>
在代码里加载并启动:
Animation fadeIn = AnimationUtils.loadAnimation(this, R.anim.fade_in);
view.startAnimation(fadeIn);
Animation scaleRotate = AnimationUtils.loadAnimation(this, R.anim.scale_rotate);
imageView.startAnimation(scaleRotate);
fillAfter="true",但即使这样,View的点击区域也不会变。我曾经因为这个bug被测试追着改了一下午……
代码方式创建补间动画
有时候动态创建更方便:
TranslateAnimation translate = new TranslateAnimation(
Animation.RELATIVE_TO_SELF, 0f,
Animation.RELATIVE_TO_SELF, 200f,
Animation.RELATIVE_TO_SELF, 0f,
Animation.RELATIVE_TO_SELF, 0f
);
translate.setDuration(500);
translate.setInterpolator(new BounceInterpolator());
view.startAnimation(translate);
嗯,这里要注意 Interpolator(插值器)。它控制动画的变化速率。默认是线性,但你可以用 AccelerateInterpolator、BounceInterpolator 等,让动画更自然。
属性动画(Property Animation)
属性动画是Android 3.0(API 11)引入的。它直接改变对象的真实属性。说白了,补间动画是“假动”,属性动画是“真动”。
核心类就一个:ObjectAnimator。用法非常简洁:
ObjectAnimator animator = ObjectAnimator.ofFloat(
view, // 目标对象
"translationX", // 属性名
0f, // 起始值
300f // 结束值
);
animator.setDuration(1000);
animator.start();
常用的属性有:
translationX/translationY— 平移rotation/rotationX/rotationY— 旋转scaleX/scaleY— 缩放alpha— 透明度x/y— 绝对位置
setTranslationX(float)。如果自定义View没有这个setter,动画会直接崩掉。我踩过这个坑,后来养成了习惯——写自定义View时先把常用属性的setter补上。
组合动画:AnimatorSet
多个动画一起播放,用 AnimatorSet:
ObjectAnimator alpha = ObjectAnimator.ofFloat(view, "alpha", 0f, 1f);
ObjectAnimator scaleX = ObjectAnimator.ofFloat(view, "scaleX", 0.5f, 1.2f);
ObjectAnimator scaleY = ObjectAnimator.ofFloat(view, "scaleY", 0.5f, 1.2f);
AnimatorSet set = new AnimatorSet();
set.playTogether(alpha, scaleX, scaleY); // 同时播放
// set.playSequentially(alpha, scaleX); // 顺序播放
set.setDuration(600);
set.start();
我个人习惯用 playTogether 做入场动画,用 playSequentially 做步骤引导动画。
ValueAnimator:更底层的控制
如果你需要更精细的控制,可以用 ValueAnimator。它不直接操作对象,而是不断回调数值:
ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
animator.setDuration(2000);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float value = (float) animation.getAnimatedValue();
// 自己控制View的变化
view.setAlpha(value);
view.setScaleX(value);
}
});
animator.start();
说白了,ObjectAnimator 是 ValueAnimator 的封装版。大部分场景用 ObjectAnimator 就够了。
补间动画 vs 属性动画
| 对比项 | 补间动画 | 属性动画 |
|---|---|---|
| 改变真实属性 | ❌ 不改变 | ✅ 改变 |
| 点击区域 | 不变 | 跟随移动 |
| 支持属性 | 仅4种 | 任意有setter的属性 |
| 性能 | 较好(只重绘) | 稍差(触发invalidate) |
| API级别 | API 1+ | API 11+ |
我的建议是:新项目直接用属性动画。除非你要兼容非常老的设备(API 11以下),或者只是做一个简单的淡入淡出效果。
知识体系总览
下面这张图帮你理清今天讲的内容:
避坑指南
最后分享几个我踩过的坑:
- 补间动画的点击事件错位 — 我曾经做了一个按钮飞入动画,结果用户怎么点都没反应。后来发现点击区域还在屏幕外。解决方案:用属性动画代替,或者手动更新点击区域。
- 属性动画内存泄漏 — 动画是无限循环的,Activity销毁时记得调用
animator.cancel()。我习惯在onDestroy()里统一清理。 - 动画卡顿 — 如果动画掉帧,检查是不是在动画过程中做了耗时操作。另外,用
ViewPropertyAnimator可以自动优化性能:view.animate().alpha(0f).translationX(100f).start();
动画这东西,多练几次就熟了。关键是理解补间动画和属性动画的本质区别——一个改“面子”,一个改“里子”。