动画简介
您可以通过动画添加视觉提示,向用户通知应用中的动态。当界面状态发生改变时(例如有新内容加载或有新操作可用时),动画尤其有用。动画还为应用增加了优美的外观,使其拥有更高品质的外观和风格。
Android 根据您需要的动画类型提供不同的动画 API,因此本页概括介绍了向界面添加动作的不同方法。
属性动画概述
属性动画系统是一个强健的框架,用于为几乎任何内容添加动画效果。您可以定义一个随时间更改任何对象属性的动画,无论其是否绘制到屏幕上。属性动画会在指定时长内更改属性(对象中的字段)的值。要添加动画效果,请指定要添加动画效果的对象属性,例如对象在屏幕上的位置、动画效果持续多长时间以及要在哪些值之间添加动画效果。
借助属性动画系统,您可以定义动画的以下特性:
- 时长:您可以指定动画的时长。默认时长为 300 毫秒。
- 时间插值:您可以指定如何根据动画的当前已播放时长来计算属性的值。
- 重复计数和行为:您可以指定是否在某个时长结束后重复播放动画以及重复播放动画多少次。您还可以指定是否要反向播放动画。如果将其设置为反向播放,则会先播放动画,然后反向播放动画,直到达到重复次数。
- Animator 集:您可以将动画分成多个逻辑集,它们可以一起播放、按顺序播放或者在指定的延迟时间后播放。
- 帧刷新延迟:您可以指定动画帧的刷新频率。默认设置为每 10 毫秒刷新一次,但应用刷新帧的速度最终取决于整个系统的繁忙程度以及系统为底层计时器提供服务的速度。
属性动画的工作原理
首先,让我们通过一个简单的示例来了解动画的工作原理。图 1 描绘了一个假设的对象,该对象的 x 属性(表示其在屏幕上的水平位置)添加了动画效果。动画时长设置为 40 毫秒,要移动的距离为 40 像素。该对象每隔 10 毫秒(这是默认的帧刷新频率)会水平移动 10 像素。在 40 毫秒时,动画停止,同时对象在水平位置 40 处停止。这是使用线性插值(表示对象以恒定速度移动)的动画示例。
也可以指定动画使用非线性插值。图 2 展示了一个假设的对象,它在动画开始时加速,在动画结束前减速。该对象仍在 40 毫秒内移动了 40 像素,但这种移动是非线性的。开始时,此动画加速移动到中间点,然后从中间点减速移动,直至动画结束。如图 2 所示,动画在开头和结尾移动的距离小于在中间移动的距离。
我们来详细了解一下属性动画系统的重要组成部分将如何计算如上所示的动画。图 3 描绘了主类之间是如何相互协作的。
ValueAnimator对象跟踪动画的时间,例如动画的已运行时长以及正在添加动画效果的属性的当前值。
ValueAnimator 包含 TimeInterpolator和 TypeEvaluator;前者用于定义动画插值,后者用于定义如何计算正在添加动画效果的属性的值。例如,在图 2 中,所用的 TimeInterpolator为 AccelerateDecelerateInterpolator,所用的 TypeEvaluator 为 IntEvaluator。
要开始动画,请创建一个 ValueAnimator,并为您想要添加动画效果的属性赋予起始值和结束值,以及动画时长。当您调用 start() 时,动画即会开始播放。在整个动画播放期间,ValueAnimator 将基于动画时长和已播放时长计算已完成动画分数(在 0 和 1 之间)。已完成动画分数表示动画已完成时间的百分比,0 表示 0%,1 表示 100%。以图 1 为例,在 t = 10ms 处,已完成动画分数将为 0.25,因为总时长 t = 40ms。
在 ValueAnimator 计算完已完成动画分数后,它会调用当前设置的TimeInterpolator 来计算插值分数。插值分数会将已完成动画分数映射为一个新分数,该分数会考虑已设置的时间插值。例如,在图 2 中,由于动画缓慢加速,t = 10ms 时的插值分数(约 0.15)小于已完成动画分数 (0.25)。在图 1 中,插值分数始终等于已完成动画分数。
计算插值分数后,ValueAnimator 会调用相应的 TypeEvaluator,以根据动画的插值分数、起始值和结束值来计算要添加动画效果的属性的值。例如,在图 2 中,t = 10ms 时的插值分数为 0.15,因此,此时属性的值为 0.15 × (40 - 0),即 6。
属性动画与试图动画的区别
视图动画系统仅提供为View对象添加动画效果的功能,因此,如果您想为非 对象添加动画效果,则必须实现自己的代码才能做到。视图动画系统也存在一些限制,因为它仅公开 对象的部分方面来供您添加动画效果;例如,您可以对视图的缩放和旋转添加动画效果,但无法对背景颜色这样做。
视图动画系统的另一个缺点是它只会在绘制视图的位置进行修改,而不会修改实际的视图本身。例如,如果您为某个按钮添加了动画效果,使其可以在屏幕上移动,该按钮会正确绘制,但能够点击按钮的实际位置并不会更改,因此您必须通过实现自己的逻辑来处理此事件。
有了属性动画系统,您就可以完全摆脱这些束缚,还可以为任何对象(视图和非视图)的任何属性添加动画效果,并且实际修改的是对象本身。属性动画系统在执行动画方面也更为强健。概括地讲,您可以为要添加动画效果的属性(例如颜色、位置或大小)分配 Animator,还可以定义动画的各个方面,例如多个 Animator 的插值和同步。
不过,视图动画系统的设置需要的时间较短,需要编写的代码也较少。如果视图动画可以完成您需要执行的所有操作,或者现有代码已按照您需要的方式运行,则无需使用属性动画系统。在某些用例中,也可以针对不同的情况同时使用这两种动画系统。
API 概述
类 | 说明 |
---|---|
ValueAnimator | 属性动画的主计时引擎,它也可计算要添加动画效果的属性的值。它具有计算动画值所需的所有核心功能,同时包含每个动画的计时详情、有关动画是否重复播放的信息、用于接收更新事件的监听器以及设置待评估自定义类型的功能。为属性添加动画效果分为两个步骤:计算添加动画效果之后的值,以及对要添加动画效果的对象和属性设置这些值。ValueAnimator 不会执行第二个步骤,因此,您必须监听由 ValueAnimator 计算的值的更新情况,并使用您自己的逻辑修改要添加动画效果的对象。如需了解详情,请参阅使用 ValueAnimator 添加动画效果部分。 |
ObjectAnimator | ValueAnimator的子类,用于设置目标对象和对象属性以添加动画效果。此类会在计算出动画的新值后相应地更新属性。在大多数情况下,您不妨使用 ObjectAnimator,因为它可以极大地简化对目标对象的值添加动画效果这一过程。不过,有时您需要直接使用 ValueAnimator,因为 ObjectAnimator存在其他一些限制,例如要求目标对象具有特定的访问器方法。 |
AnimatorSet | 此类提供一种将动画分组在一起的机制,以使它们彼此相对运行。您可以将动画设置为一起播放、按顺序播放或者在指定的延迟时间后播放。如需了解详情,请参阅使用 AnimatorSet 编排多个动画部分。 |
评估程序负责告知属性动画系统如何计算指定属性的值。它们使用由 Animator 类提供的计时数据(即动画的起始值和结束值),并根据这些数据计算属性添加动画效果之后的值。属性动画系统可提供以下评估程序:
类/接口 | 说明 |
---|---|
IntEvaluator | 这是用于计算 int 属性的值的默认评估程序。 |
FloatEvaluator | 这是用于计算 float 属性的值的默认评估程序。 |
ArgbEvaluator | 这是用于计算颜色属性的值(用十六进制值表示)的默认评估程序。 |
TypeEvaluator | 此接口用于创建您自己的评估程序。如果您要添加动画效果的对象属性不是 int 、float 或颜色,那么您必须实现 TypeEvaluator 接口,才能指定如何计算对象属性添加动画效果之后的值。如果您想以不同于默认行为的方式处理 int 、float 和颜色,您还可以为这些类型的值指定自定义 TypeEvaluator。如需详细了解如何编写自定义评估程序,请参阅使用 TypeEvaluator 部分。 |
时间插值器指定了如何根据时间计算动画中的特定值。例如,您可以指定动画在整个动画中以线性方式播放,即动画在整个播放期间匀速移动;也可以指定动画使用非线性时间,例如动画在开始后加速并在结束前减速。表 3 介绍了 android.view.animation 中包含的插值器。如果下表提供的插值器都不能满足您的需求,请实现 TimeInterpolator 接口并创建您自己的插值器。如需详细了解如何编写自定义插值器,请参阅 使用插值器 。
类/接口 | 说明 |
---|---|
AccelerateDecelerateInterpolator | 该插值器的变化率在开始和结束时缓慢但在中间会加快。 |
AccelerateInterpolator | 该插值器的变化率在开始时较为缓慢,然后会加快。 |
AnticipateInterpolator | 该插值器先反向变化,然后再急速正向变化。 |
AnticipateOvershootInterpolator | 该插值器先反向变化,再急速正向变化,然后超过定位值,最后返回到最终值。 |
BounceInterpolator | 该插值器的变化会跳过结尾处。 |
CycleInterpolator | 该插值器的动画会在指定数量的周期内重复。 |
DecelerateInterpolator | 该插值器的变化率开始很快,然后减速。 |
LinearInterpolator | 该插值器的变化率恒定不变。 |
OvershootInterpolator | 该插值器会急速正向变化,再超出最终值,然后返回。 |
TimeInterpolator | 该接口用于实现您自己的插值器。 |
使用AnimatorSet 编排多个动画
在许多情况下,您需要根据一个动画开始或结束的时间来播放另一个动画。借助 Android 系统,您可以将动画捆绑到一个 AnimatorSet 中,以便指定是同时播放动画、按顺序播放还是在指定的延迟时间后播放。您还可以相互嵌套 AnimatorSet 对象。
以下代码段通过以下方式播放相应的 Animator 对象:
- 播放 bounceAnim。
- 同时播放 squashAnim1、squashAnim2、stretchAnim1 和 stretchAnim2。
- 播放 bounceBackAnim。
- 播放 fadeAnim。
AnimatorSet bouncer = new AnimatorSet();
bouncer.play(bounceAnim).before(squashAnim1);
bouncer.play(squashAnim1).with(squashAnim2);
bouncer.play(squashAnim1).with(stretchAnim1);
bouncer.play(squashAnim1).with(stretchAnim2);
bouncer.play(bounceBackAnim).after(stretchAnim2);
ValueAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f);
fadeAnim.setDuration(250);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.play(bouncer).before(fadeAnim);
animatorSet.start();