2、View动画之补间动画:AlphaAnimation、ScaleAnimation、TranslateAnimation、RotateAnimation

补间动画,说白了就是「我只管定义动画的起止状态,中间怎么变交给系统」。你不需要像帧动画那样一张张切图,只需要告诉 View:从哪开始、到哪结束、持续多久,剩下的 Android 帮你算。

我个人习惯把补间动画叫做「四件套」——Alpha、Scale、Translate、Rotate。这四种动画几乎覆盖了早期 Android 界面动效的全部需求。我记得刚入行那会儿,一个列表的 Item 进场动画,全靠这四个兄弟排列组合。

2.1 补间动画的核心机制

补间动画的本质,是 对 View 的矩阵变换做插值。系统根据你设定的起始值、结束值、插值器(Interpolator)和时长,在每一帧计算出中间状态,然后应用到 View 的绘制矩阵上。

嗯,这里要注意:补间动画改变的只是 View 的绘制位置和外观,不会改变 View 的布局属性。比如你把一个 Button 用 TranslateAnimation 移到了屏幕右边,点击事件仍然在原来的位置响应。我在项目中遇到过这个坑——用户明明点到了按钮,却没有任何反应,排查了半天才发现是动画移动了视觉位置,但触摸区域还在原地。

⚠️ 重要提醒: 补间动画结束后,View 会「回弹」到原始位置。除非你设置 fillAfter = true,否则动画一停,一切恢复原样。

2.2 四种动画详解

2.2.1 AlphaAnimation(透明度动画)

控制 View 从透明到不透明,或者反过来。参数很简单:fromAlphatoAlpha,取值范围 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 轴上的缩放比例。参数有 fromXtoXfromYtoY,以及缩放中心点 pivotXpivotY

// 从 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 绕指定中心点旋转。参数:fromDegreestoDegrees、旋转中心。

// 从 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 知识体系总览

下面这张图帮你理清补间动画的脉络:

补间动画知识体系 补间动画 AlphaAnimation ScaleAnimation TranslateAnimation RotateAnimation 关键参数 from / to 起始结束值 pivot 旋转/缩放中心 duration 持续时间 扩展特性 AnimationSet 组合动画 Interpolator 插值器 fillAfter / fillBefore 注意:补间动画不改变 View 实际布局属性,仅改变绘制效果

2.6 避坑指南

做补间动画这么多年,我踩过不少坑。挑几个典型的说说:

  • 动画结束后 View 回弹:记得设置 setFillAfter(true),否则动画一停,View 瞬间回到原始位置,看起来很突兀。
  • 触摸事件错位:补间动画不改变 View 的布局位置,所以触摸区域还在原地。如果你需要「真正移动」View,建议用属性动画或者直接修改 LayoutParams。
  • 性能问题:补间动画在 Android 3.0 之前是唯一的选择,但它会触发 View 的 invalidate(),如果动画复杂或者 View 层级深,容易掉帧。我建议尽量用硬件加速(在 Manifest 中开启 android:hardwareAccelerated="true")。
  • 动画叠加:多次调用 startAnimation() 会重置之前的动画。如果你想让多个动画同时运行,一定要用 AnimationSet
💡 个人经验: 补间动画虽然「老」,但在简单场景下依然好用。比如加载提示、按钮反馈、列表入场等。不需要引入复杂的三方库,原生就能搞定。不过如果你的项目已经用上了 Compose,那还是建议用 Compose 的动画系统,更灵活、更高效。

好了,补间动画的四件套就聊到这儿。下一节我们会看看属性动画——它解决了补间动画的很多痛点,也是 Android 动画演进中重要的一步。


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