动画与过渡:让APP“活”起来

说实话,动画是APP的灵魂。没有动画的APP,就像一杯白开水——能喝,但没滋味。我刚开始做Android开发时,总觉得动画是锦上添花的东西,直到有一次给客户演示原型,对方说:“你这按钮点下去没反应啊?”其实有反应,只是没动画反馈。嗯,从那以后,我把动画提到了和功能同等重要的位置。

今天咱们聊聊Android里的动画体系。说白了,就是怎么让界面元素动起来,怎么让页面切换更丝滑。我会把实际项目中的经验揉进去,你跟着走一遍,基本就能应付大部分动画需求了。

Android动画体系总览 Android动画 View动画(Tween) 属性动画(Property) 帧动画(Frame) Activity过渡动画 Lottie动画库 如何选择? 简单变换 → View动画 复杂属性变化 → 属性动画 序列帧 → 帧动画 | 页面切换 → 过渡动画 | 复杂动效 → Lottie

View动画(Tween Animation)

View动画是Android最早期的动画方案。它只对View的视觉效果做变换,不改变View的实际属性。什么意思呢?比如你把一个按钮平移了100像素,它看起来是移动了,但点击区域还在原地。我在项目中就踩过这个坑——用户点不到按钮,因为实际位置没变。

View动画支持四种基本变换:

动画类型 XML标签 作用
缩放 scale 放大或缩小
旋转 rotate 绕中心或指定点旋转
平移 translate 沿X或Y轴移动
透明度 alpha 从透明到不透明

来个实际例子。假设你想让一个图片从左侧滑入:

<!-- res/anim/slide_in_left.xml -->
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:fromXDelta="-100%"
        android:toXDelta="0%"
        android:duration="500" />
    <alpha
        android:fromAlpha="0.0"
        android:toAlpha="1.0"
        android:duration="500" />
</set>

在代码里这样用:

Animation anim = AnimationUtils.loadAnimation(this, R.anim.slide_in_left);
imageView.startAnimation(anim);
⚠️ 注意:View动画结束后,View会回到原始位置。如果你希望保持动画结束时的状态,记得设置 android:fillAfter="true"。我曾经因为这个被测试妹子追着改bug,说按钮“跳来跳去”。

属性动画(Property Animation)

属性动画是View动画的升级版。它真正改变对象的属性值,而不是只改视觉效果。说白了,你用属性动画移动一个按钮,它的点击区域也会跟着走。这才是“真·动画”。

核心类是 ValueAnimatorObjectAnimator。我个人习惯用 ObjectAnimator,因为它更直接:

ObjectAnimator animator = ObjectAnimator.ofFloat(
    myView, 
    "translationX", 
    0f, 300f
);
animator.setDuration(1000);
animator.start();

你想想看,这比View动画灵活多了。你可以对任何对象的任何属性做动画,只要它有setter方法。比如自定义View里的 progress 属性,或者背景颜色:

ObjectAnimator colorAnim = ObjectAnimator.ofObject(
    myView,
    "backgroundColor",
    new ArgbEvaluator(),
    0xFF0000, 0x00FF00
);
colorAnim.setDuration(2000);
colorAnim.start();
💡 小技巧:AnimatorSet 可以把多个动画组合起来,实现“同时播放”、“顺序播放”等效果。我在做登录页面的Logo动画时,就用它实现了“先放大再旋转”的效果,用户体验直接拉满。

帧动画(Frame Animation)

帧动画,说白了就是“翻书动画”。你把一张张图片按顺序播放,看起来就像在动。适合做加载中的小菊花、或者一些简单的逐帧效果。

res/drawable 下定义:

<!-- loading_animation.xml -->
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="false">
    <item android:drawable="@drawable/frame_01" android:duration="100" />
    <item android:drawable="@drawable/frame_02" android:duration="100" />
    <item android:drawable="@drawable/frame_03" android:duration="100" />
    <item android:drawable="@drawable/frame_04" android:duration="100" />
</animation-list>

代码里启动:

ImageView loadingView = findViewById(R.id.loading);
loadingView.setImageResource(R.drawable.loading_animation);
AnimationDrawable anim = (AnimationDrawable) loadingView.getDrawable();
anim.start();
⚠️ 注意:帧动画的每一帧都是一张图片,如果帧数多或者图片大,很容易OOM。我曾经在低端机上测试,一个30帧的加载动画直接让APP崩了。后来改用Lottie,问题就解决了。

Activity过渡动画

Activity之间的切换动画,说白了就是“翻页效果”。Android提供了两种方式:

1. 主题中定义:

<style name="AppTheme" parent="Theme.AppCompat.Light">
    <item name="android:windowEnterTransition">@transition/slide_right</item>
    <item name="android:windowExitTransition">@transition/slide_left</item>
</style>

2. 代码中调用:

startActivity(intent);
overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_left);

我个人更推荐用 Material Design 的共享元素过渡。比如两个Activity里都有同一张图片,你可以让它在切换时“飞”过去,效果非常酷:

// Activity A
Intent intent = new Intent(this, DetailActivity.class);
ActivityOptionsCompat options = ActivityOptionsCompat
    .makeSceneTransitionAnimation(this, imageView, "shared_image");
startActivity(intent, options.toBundle());

// Activity B 的布局中
<ImageView
    android:transitionName="shared_image"
    ... />

Lottie动画库

Lottie是Airbnb开源的一个动画库。它把设计师用After Effects做的动画导出成JSON文件,然后直接在APP里渲染。说白了,设计师做什么,APP就显示什么,几乎零损耗。

为什么我强烈推荐它?因为它解决了动画开发的终极难题——设计师和开发者的沟通成本。以前设计师给一个动效,我得用代码一点一点调,调完还不一定像。现在好了,设计师导出JSON,我直接扔进去:

// 添加依赖:implementation 'com.airbnb.android:lottie:6.0.0'

// 布局中
<com.airbnb.lottie.LottieAnimationView
    android:id="@+id/lottie_view"
    android:layout_width="200dp"
    android:layout_height="200dp"
    app:lottie_rawRes="@raw/loading_animation"
    app:lottie_loop="true"
    app:lottie_autoPlay="true" />

代码控制也很简单:

LottieAnimationView lottieView = findViewById(R.id.lottie_view);
lottieView.setAnimation(R.raw.confetti);
lottieView.playAnimation();

// 监听动画进度
lottieView.addAnimatorUpdateListener(animation -> {
    float progress = animation.getAnimatedValueAbsolute();
    // 根据进度做其他事情
});
💡 我的经验:Lottie文件大小通常只有几十KB,比帧动画小太多了。而且它支持动态修改颜色、控制播放速度。我在一个电商项目里,用Lottie做了“加入购物车”的飞线动画,用户反馈说“感觉购物车真的在动”,转化率都提升了。

如何选择?一张表说清楚

场景 推荐方案 原因
简单缩放/旋转/平移 View动画 轻量,代码少
需要改变实际属性 属性动画 真正改变对象状态
简单逐帧效果 帧动画 实现简单,但注意OOM
页面切换 过渡动画 系统原生支持,体验好
复杂动效/设计师主导 Lottie 还原度高,文件小

最后说一句:动画不是越多越好。我见过有些APP,每个按钮都弹一下,每个页面都转一圈,看得人眼花。记住一个原则——动画是为了引导用户,不是为了炫技。该动的时候动,不该动的时候别动,这才是高手。


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