2、View动画之补间动画:AlphaAnimation、ScaleAnimation、TranslateAnimation、RotateAnimation
补间动画,说白了就是「我只管定义动画的起止状态,中间怎么变交给系统」。你不需要像帧动画那样一张张切图,只需要告诉 View:从哪开始、到哪结束、持续多久,剩下的 Android 帮你算。
我个人习惯把补间动画叫做「四件套」——Alpha、Scale、Translate、Rotate。这四种动画几乎覆盖了早期 Android 界面动效的全部需求。我记得刚入行那会儿,一个列表的 Item 进场动画,全靠这四个兄弟排列组合。
2.1 补间动画的核心机制
补间动画的本质,是 对 View 的矩阵变换做插值。系统根据你设定的起始值、结束值、插值器(Interpolator)和时长,在每一帧计算出中间状态,然后应用到 View 的绘制矩阵上。
嗯,这里要注意:补间动画改变的只是 View 的绘制位置和外观,不会改变 View 的布局属性。比如你把一个 Button 用 TranslateAnimation 移到了屏幕右边,点击事件仍然在原来的位置响应。我在项目中遇到过这个坑——用户明明点到了按钮,却没有任何反应,排查了半天才发现是动画移动了视觉位置,但触摸区域还在原地。
fillAfter = true,否则动画一停,一切恢复原样。
2.2 四种动画详解
2.2.1 AlphaAnimation(透明度动画)
控制 View 从透明到不透明,或者反过来。参数很简单:fromAlpha 和 toAlpha,取值范围 0.0 ~ 1.0。
// 从完全透明到完全不透明,持续 1 秒
AlphaAnimation alpha = new AlphaAnimation(0.0f, 1.0f);
alpha.setDuration(1000);
view.startAnimation(alpha);
我曾经用这个做过一个「呼吸灯」效果——一个图标每隔 2 秒从 0.3 透明度渐变到 1.0,再变回去。配合 setRepeatMode(REVERSE) 和 setRepeatCount(INFINITE),效果很自然。
2.2.2 ScaleAnimation(缩放动画)
控制 View 在 X 轴和 Y 轴上的缩放比例。参数有 fromX、toX、fromY、toY,以及缩放中心点 pivotX、pivotY。
// 从 0.5 倍缩放到 1.5 倍,以 View 中心为缩放点
ScaleAnimation scale = new ScaleAnimation(
0.5f, 1.5f, // X 轴起始和结束
0.5f, 1.5f, // Y 轴起始和结束
Animation.RELATIVE_TO_SELF, 0.5f, // pivotX:自身宽度的 50%
Animation.RELATIVE_TO_SELF, 0.5f // pivotY:自身高度的 50%
);
scale.setDuration(800);
view.startAnimation(scale);
你想想看,如果不指定 pivot,默认是 View 的左上角 (0,0)。那缩放效果就像是从左上角「拉扯」出来,而不是从中心展开。我刚开始学的时候经常忽略这个参数,结果动画看起来总是「歪」的。
2.2.3 TranslateAnimation(位移动画)
让 View 在 X 轴和 Y 轴方向上移动。参数支持绝对像素值和相对值(相对于自身或父容器)。
// 从当前位置向右移动 200 像素,向下移动 100 像素
TranslateAnimation translate = new TranslateAnimation(
Animation.ABSOLUTE, 0, // fromXDelta
Animation.ABSOLUTE, 200, // toXDelta
Animation.ABSOLUTE, 0, // fromYDelta
Animation.ABSOLUTE, 100 // toYDelta
);
translate.setDuration(600);
view.startAnimation(translate);
我个人习惯用 RELATIVE_TO_PARENT 来做屏幕适配。比如让一个 View 从屏幕外滑入,用父容器宽度的百分比来定义位移量,这样在不同尺寸的屏幕上效果一致。
2.2.4 RotateAnimation(旋转动画)
控制 View 绕指定中心点旋转。参数:fromDegrees、toDegrees、旋转中心。
// 从 0 度旋转到 360 度,绕 View 中心旋转
RotateAnimation rotate = new RotateAnimation(
0, 360,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f
);
rotate.setDuration(1500);
view.startAnimation(rotate);
这里有个小技巧:如果你想让 View 连续旋转,可以设置 setRepeatCount(INFINITE)。但注意,每次旋转结束后会有一个「跳帧」的感觉。我曾经用 LinearInterpolator 配合无限重复,做成了一个加载转圈效果,比用帧动画省资源多了。
2.3 组合动画:AnimationSet
实际项目中很少只用一种动画。比如一个按钮「弹入」效果,需要同时缩放和透明度变化。这时候就用 AnimationSet。
AnimationSet set = new AnimationSet(true); // true 表示共享插值器
AlphaAnimation alpha = new AlphaAnimation(0.0f, 1.0f);
alpha.setDuration(500);
ScaleAnimation scale = new ScaleAnimation(0.0f, 1.0f, 0.0f, 1.0f,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
scale.setDuration(500);
set.addAnimation(alpha);
set.addAnimation(scale);
view.startAnimation(set);
注意 AnimationSet 的构造参数 shareInterpolator。如果设为 true,所有子动画共用同一个插值器;设为 false,每个子动画可以单独设置。我建议大部分场景设为 true,这样动画节奏统一,看起来更协调。
2.4 插值器:让动画更有「质感」
补间动画默认是线性插值,也就是匀速变化。但真实世界的运动哪有匀速的?
Android 提供了多种插值器:
| 插值器 | 效果 | 适用场景 |
|---|---|---|
| LinearInterpolator | 匀速 | 进度条、加载转圈 |
| AccelerateInterpolator | 先慢后快 | 物体掉落、弹出 |
| DecelerateInterpolator | 先快后慢 | 物体停止、滑入 |
| AccelerateDecelerateInterpolator | 先慢后快再慢 | 页面切换、弹跳 |
| BounceInterpolator | 末尾弹跳 | 提示消息、通知 |
// 给动画加上弹跳效果
TranslateAnimation translate = new TranslateAnimation(0, 0, -200, 0);
translate.setDuration(800);
translate.setInterpolator(new BounceInterpolator());
view.startAnimation(translate);
我曾经用 OvershootInterpolator 做过一个「过头再回弹」的效果——按钮放大到 1.2 倍,再回弹到 1.0 倍。视觉上比直接缩放到 1.0 倍更有「弹性」。
2.5 知识体系总览
下面这张图帮你理清补间动画的脉络:
2.6 避坑指南
做补间动画这么多年,我踩过不少坑。挑几个典型的说说:
- 动画结束后 View 回弹:记得设置
setFillAfter(true),否则动画一停,View 瞬间回到原始位置,看起来很突兀。 - 触摸事件错位:补间动画不改变 View 的布局位置,所以触摸区域还在原地。如果你需要「真正移动」View,建议用属性动画或者直接修改 LayoutParams。
- 性能问题:补间动画在 Android 3.0 之前是唯一的选择,但它会触发 View 的
invalidate(),如果动画复杂或者 View 层级深,容易掉帧。我建议尽量用硬件加速(在 Manifest 中开启android:hardwareAccelerated="true")。 - 动画叠加:多次调用
startAnimation()会重置之前的动画。如果你想让多个动画同时运行,一定要用AnimationSet。
好了,补间动画的四件套就聊到这儿。下一节我们会看看属性动画——它解决了补间动画的很多痛点,也是 Android 动画演进中重要的一步。