属性动画概览

试试 Compose 方式
Jetpack Compose 是推荐在 Android 设备上使用的界面工具包。了解如何在 Compose 中使用动画。
<ph type="x-smartling-placeholder"></ph> 设置动画*AsState →

属性动画系统是一个稳健的框架, 为几乎任何事物添加动画效果。您可以定义一个动画,以便随时间更改任何对象属性, 而不管它是否会在屏幕上绘制属性动画会更改属性的 (对象中的字段)值。如需为某个对象添加动画效果,您需要指定要添加动画效果的对象属性,例如对象在屏幕上的位置、动画效果持续多长时间以及要在哪些值之间添加动画效果。

借助属性动画系统,您可以定义动画的以下特性:

  • 时长:您可以指定动画的时长。默认时长为 300 毫秒。
  • 时间插值:您可以指定如何根据动画的当前已播放时长来计算属性的值。
  • 重复计数和行为:您可以指定在发生以下情况时是否重复播放动画: 播放动画。您还可以指定是否要反向播放动画。将其设置为反向播放 动画先快进再反复播放,直到达到重复次数。
  • Animator 集:您可以将动画分成多个逻辑集,它们可以一起播放、按顺序播放或者在指定的延迟时间后播放。
  • 帧刷新延迟:您可以指定动画帧的刷新频率。默认设置为每 10 毫秒刷新一次,但应用刷新帧的速度最终取决于整个系统的繁忙程度以及系统为底层计时器提供服务的速度。

如需查看属性动画的完整示例,请参阅 GitHub 上 CustomTransition 示例中的 ChangeColor 类。

属性动画的工作原理

首先,让我们通过一个简单的示例来了解动画的工作原理。图 1 描绘了一个假设的对象,该对象的 x 属性(表示其在屏幕上的水平位置)添加了动画效果。动画时长设置为 40 毫秒,距离 移动距离为 40 像素该对象每隔 10 毫秒(这是默认的帧刷新频率)会水平移动 10 像素。在 40 毫秒时,动画停止,同时对象在水平位置 40 处停止。这是使用线性插值(表示对象以恒定速度移动)的动画示例。

图 1. 线性动画示例

您也可以指定动画使用非线性插值。图 2 展示了一个假设的对象,它在动画开始时加速,在动画结束前减速。该对象仍在 40 毫秒内移动了 40 像素,但这种移动是非线性的。开始时,此动画加速移动到中间点,然后从中间点减速移动,直至动画结束。如图 2 所示,经过的距离和 动画开头和结尾的值小于中间值。

图 2. 非线性动画示例

我们来详细了解一下属性动画系统的重要组成部分将如何计算如上所示的动画。图 3 描绘了主类之间是如何相互协作的。

图 3. 如何计算动画

ValueAnimator 对象会跟踪动画的时间设置, 例如动画已经运行了多长时间,以及它所具有的属性的当前值。 动画效果

ValueAnimator 包含 TimeInterpolatorTypeEvaluator;前者用于定义动画插值,后者用于定义如何计算正在添加动画效果的属性的值。例如,在图 2 中,使用的 TimeInterpolatorAccelerateDecelerateInterpolatorTypeEvaluatorIntEvaluator

如需启动动画,请创建一个 ValueAnimator 并为其指定 要添加动画效果的属性的起始值和结束值,以及 动画。当您调用 start() 时,动画即会开始播放。在整个动画播放期间,ValueAnimator 会计算已用分数 介于 0 和 1 之间,具体取决于动画的时长和已过去的时间。已完成动画分数表示动画已完成时间的百分比,0 表示 0%,1 表示 100%。例如,在图 1 中,t = 10 毫秒时经过的分数为 0 .25 因为总时长为 t = 40 毫秒。

ValueAnimator 计算完已完成动画分数后,它会调用当前设置的 TimeInterpolator 来计算插值分数。插值分数会将已完成分数映射到新的分数 将所设置的时间插值考虑在内的比例。例如,在图 2 中, 由于动画缓慢加速,因此插值分数(约 0 .15)会小于 已用分数 (0.25),t = 10 毫秒。在图 1 中,插值分数始终与 已完成的部分。

计算插值分数时,ValueAnimator 会调用 相应的 TypeEvaluator,以计算 您要为之添加动画效果的属性,该元素根据插值分数、起始值和 动画的结束值。例如,在图 2 中,t = 10ms 时的插值分数为 0.15,因此,此时属性的值为 0.15 × (40 - 0),即 6。

属性动画与视图动画的区别

视图动画系统提供仅为 View 添加动画效果的功能 因此,如果您想为非 View 对象添加动画效果,则必须实现 编写自己的代码视图动画系统也受到了限制,因为 公开了要添加动画效果的 View 对象的多个方面,例如缩放和 旋转视图,但不改变背景颜色。

视图动画系统的另一个缺点是它只会在绘制视图的位置进行修改,而不会修改实际的视图本身。例如,如果您为某个用于移动的按钮添加了动画效果 在屏幕上,按钮可以正确绘制,但是可以点击按钮的实际位置 按钮不会发生变化,因此您必须实现自己的逻辑来处理这种情况。

借助属性动画系统,这些约束条件会被完全消除,并且您可以为 任何对象(View 和非 View)的任何属性,并且对象本身实际上已经过修改。 属性动画系统在执行动画方面也更为强健。概括地讲,您可以为要添加动画效果的属性(例如颜色、位置或大小)分配 Animator,还可以定义动画的各个方面,例如多个 Animator 的插值和同步。

不过,视图动画系统的设置时间较短,需要编写的代码也较少。 如果视图动画完成了您需要执行的所有操作,或者您的现有代码已经完成, 因此无需使用属性动画系统。它也可能会 如果出现使用情形,可以针对不同情况同时使用这两种动画系统。

API 概览

您可以在 android.animation 中找到属性动画系统的大多数 API。由于视图动画系统已经在 android.view.animation 中定义了许多插值器,因此,您也可以在属性动画系统中使用这些插值器。下表介绍了属性动画系统的主要组成部分。

Animator 类提供了创建动画的基本结构。您通常不会直接使用此类,因为它只提供极少的功能,这些功能必须经过扩展才能全面支持为值添加动画效果。以下子类可扩展 Animator

表 1. Animator

说明
ValueAnimator 属性动画的主计时引擎,也会为 属性。它包含计算动画的所有核心功能 值,并包含每个动画的时间详情、有关 动画重复次数、接收更新事件的监听器、设置自定义 要评估的类型。为属性添加动画效果分为两个步骤:计算添加动画效果之后的值,以及对要添加动画效果的对象和属性设置这些值。ValueAnimator 不执行第二部分,所以您必须听 用于更新由 ValueAnimator 和 使用您自己的逻辑修改要添加动画效果的对象。如需了解详情,请参阅使用 ValueAnimator 添加动画效果部分。
ObjectAnimator ValueAnimator 的子类,允许您设置目标 对象和对象属性添加动画效果。当发生以下情况时,该类会相应地更新属性: 将为动画计算一个新值。您想使用 ObjectAnimator大多数时候, 因为它可以大大简化为目标对象上的值添加动画效果的过程。不过,有时您需要直接使用 ValueAnimator,因为 ObjectAnimator 存在其他一些限制,例如要求目标对象具有特定的访问器方法。
AnimatorSet 此类提供一种将动画分组在一起的机制,以使它们彼此相对运行。您可以将动画设置为一起播放、依序播放或播放后播放 指定延迟时间。请参阅编排多个 动画效果

评估程序负责告知属性动画系统如何计算指定属性的值。它们会获取 Animator 提供的时间数据 类设置动画的起始值和结束值,并计算属性的动画值 生成自定义文本。属性动画系统可提供以下评估程序:

表 2. 评估程序

类/接口 说明
IntEvaluator 这是用于计算 int 属性的值的默认评估程序。
FloatEvaluator 这是用于计算 float 属性的值的默认评估程序。
ArgbEvaluator 用于计算所表示颜色属性的值的默认评估程序 以十六进制值表示。
TypeEvaluator 此接口用于创建您自己的评估程序。如果您要为 不是 intfloat 或 color 的对象属性, 您必须实现 TypeEvaluator 接口,以指定 计算对象属性的动画值。您还可以为 intfloat 和颜色指定自定义 TypeEvaluator 值。 如需详细了解如何编写自定义评估程序,请参阅使用 TypeEvaluator 部分。

时间插值器定义了动画中的特定值如何以 时间函数。例如,您可以指定在整个 即“Animation”(即动画在整个播放期间均匀移动),也可以指定动画 使用非线性时间,例如,在开始时加速并在 动画结束。表 3 介绍了 android.view.animation 中包含的插值器。如果提供的插值器都不合适 实现 TimeInterpolator 接口并创建您自己的接口。如需详细了解如何编写自定义插值器,请参阅使用插值器

表 3. 插值器

类/接口 说明
AccelerateDecelerateInterpolator 该插值器的变化率在开始和结束时缓慢,但在中间会加快。
AccelerateInterpolator 该插值器的变化率在开始时较为缓慢,然后会加快。
AnticipateInterpolator 该插值器先反向变化,然后再急速正向变化。
AnticipateOvershootInterpolator 该插值器先反向变化,再急速正向变化,然后超过定位值,最后返回到最终值。
BounceInterpolator 该插值器的变化会跳过结尾处。
CycleInterpolator 该插值器的动画会在指定数量的周期内重复。
DecelerateInterpolator 该插值器的变化率在开始时很快,然后 减速。
LinearInterpolator 该插值器的变化率恒定不变。
OvershootInterpolator 该插值器会急速正向变化,再超出最终值,然后返回。
TimeInterpolator 该接口用于实现您自己的插值器。

使用 ValueAnimator 添加动画效果

借助 ValueAnimator 类,您可以为动画播放期间某些类型的值添加动画效果,只需指定一组要添加动画效果的 intfloat 或颜色值即可。您可以通过调用 ValueAnimator 的任一工厂方法来获取它:ofInt()ofFloat()ofObject()。例如:

Kotlin

ValueAnimator.ofFloat(0f, 100f).apply {
    duration = 1000
    start()
}

Java

ValueAnimator animation = ValueAnimator.ofFloat(0f, 100f);
animation.setDuration(1000);
animation.start();

在上述代码中,当 start() 方法运行时,ValueAnimator 会开始计算 1000ms 时长内 0 和 100 之间的动画的值。

您还可以通过执行以下操作来指定要添加动画效果的自定义类型:

Kotlin

ValueAnimator.ofObject(MyTypeEvaluator(), startPropertyValue, endPropertyValue).apply {
    duration = 1000
    start()
}

Java

ValueAnimator animation = ValueAnimator.ofObject(new MyTypeEvaluator(), startPropertyValue, endPropertyValue);
animation.setDuration(1000);
animation.start();

在此代码中,ValueAnimator 开始计算 startPropertyValueendPropertyValue 之间使用 当 start() 方法运行时,由 MyTypeEvaluator 提供的 1000 毫秒时长的逻辑。

您可以通过向 ValueAnimator 对象添加 AnimatorUpdateListener 来使用动画的值,如以下代码所示:

Kotlin

ValueAnimator.ofObject(...).apply {
    ...
    addUpdateListener { updatedAnimation ->
        // You can use the animated value in a property that uses the
        // same type as the animation. In this case, you can use the
        // float value in the translationX property.
        textView.translationX = updatedAnimation.animatedValue as Float
    }
    ...
}

Java

animation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator updatedAnimation) {
        // You can use the animated value in a property that uses the
        // same type as the animation. In this case, you can use the
        // float value in the translationX property.
        float animatedValue = (float)updatedAnimation.getAnimatedValue();
        textView.setTranslationX(animatedValue);
    }
});

onAnimationUpdate()中 方法,您可以访问更新后的动画值,并将其用于 您的某个数据视图如需详细了解监听器,请参阅动画监听器部分。

使用 ObjectAnimator 添加动画效果

ObjectAnimator 是上一部分中讨论的 ValueAnimator 的子类,它融合了 ValueAnimator 的计时引擎和值计算以及为目标对象的命名属性添加动画效果这一功能。这大大简化了为任何对象添加动画效果的过程, 不再需要实现 ValueAnimator.AnimatorUpdateListener, 因为动画属性会自动更新。

实例化 ObjectAnimatorValueAnimator 的过程类似,但您也可以指定对象和该对象属性的名称(以字符串形式),以及要在哪些值之间添加动画效果:

Kotlin

ObjectAnimator.ofFloat(textView, "translationX", 100f).apply {
    duration = 1000
    start()
}

Java

ObjectAnimator animation = ObjectAnimator.ofFloat(textView, "translationX", 100f);
animation.setDuration(1000);
animation.start();

要使 ObjectAnimator 正确更新属性,您必须执行以下操作:

  • 要添加动画效果的对象属性必须具有 set<PropertyName>() 形式的 setter 函数(采用驼峰式大小写形式)。因为 ObjectAnimator 在动画播放期间自动更新属性,它必须能够访问该属性 使用此 setter 方法。例如,如果属性名称为 foo,则需要使用 setFoo() 方法。如果此 setter 方法不存在,您将得到三个 选项: <ph type="x-smartling-placeholder">
      </ph>
    • 如果您有权限,可将 setter 方法添加到类中。
    • 使用您有权更改的封装容器类,让该封装容器使用有效的 setter 方法接收值并将其转发给原始对象。
    • 改用 ValueAnimator
  • 如果您在某个 ObjectAnimator 工厂方法中仅为 values... 形参指定一个值,则系统会假定该值是 动画。因此,要添加动画效果的对象属性必须具有用于获取动画起始值的 getter 函数。getter 函数必须位于 采用 get<PropertyName>() 格式。例如,如果属性名称为 foo,则需要使用 getFoo() 方法。
  • 要添加动画效果的属性的 getter(如果需要)和 setter 方法的操作对象必须与您为 ObjectAnimator 指定的起始值和结束值的类型相同。例如,您必须 targetObject.setPropName(float)targetObject.getPropName() 如果您构造以下 ObjectAnimator
    ObjectAnimator.ofFloat(targetObject, "propName", 1f)
  • 根据要添加动画效果的属性或对象,您可能需要对 View 调用 invalidate() 方法,以强制屏幕使用 已更新动画值。您可以在 onAnimationUpdate() 回调。例如,为可绘制对象的 color 属性添加动画效果只会导致系统对 重新绘制自身时返回屏幕。视图的所有属性 setter(如 setAlpha()setTranslationX())都会使视图失效,因此,在使用新值调用这些方法时,您无需使视图失效。如需详细了解监听器,请参阅动画监听器部分。

使用 AnimatorSet 编排多个动画

在许多情况下,您需要根据一个动画开始或结束的时间来播放另一个动画。借助 Android 系统,您可以将动画捆绑到一个 AnimatorSet 中,以便指定是同时播放动画、按顺序播放还是在指定的延迟时间后播放。您还可以相互嵌套 AnimatorSet 对象。

以下代码段会播放下面的 Animator 对象:

  1. 播放 bounceAnim
  2. 同时播放 squashAnim1squashAnim2stretchAnim1stretchAnim2
  3. 播放 bounceBackAnim
  4. 播放 fadeAnim

Kotlin

val bouncer = AnimatorSet().apply {
    play(bounceAnim).before(squashAnim1)
    play(squashAnim1).with(squashAnim2)
    play(squashAnim1).with(stretchAnim1)
    play(squashAnim1).with(stretchAnim2)
    play(bounceBackAnim).after(stretchAnim2)
}
val fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f).apply {
    duration = 250
}
AnimatorSet().apply {
    play(bouncer).before(fadeAnim)
    start()
}

Java

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();

动画监听器

您可以使用下述监听器来监听动画播放期间的重要事件。

如果您不一定需要实现 Animator.AnimatorListener 接口的所有方法,则可以扩展 AnimatorListenerAdapter 类,而非实现 Animator.AnimatorListener 接口。AnimatorListenerAdapter 类提供空 您可以选择替换的方法的实现。

例如,以下代码段会创建一个 AnimatorListenerAdapter 仅针对 onAnimationEnd() 回调:

Kotlin

ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f).apply {
    duration = 250
    addListener(object : AnimatorListenerAdapter() {
        override fun onAnimationEnd(animation: Animator) {
            balls.remove((animation as ObjectAnimator).target)
        }
    })
}

Java

ValueAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f);
fadeAnim.setDuration(250);
fadeAnim.addListener(new AnimatorListenerAdapter() {
public void onAnimationEnd(Animator animation) {
    balls.remove(((ObjectAnimator)animation).getTarget());
}

为 ViewGroup 对象的布局更改添加动画效果

属性动画系统提供为 ViewGroup 对象更改添加动画效果的功能 还可以轻松为 View 对象本身添加动画效果。

您可使用 LayoutTransition 类为 ViewGroup 内的布局更改添加动画效果。当您向 ViewGroup 添加视图或删除其中的视图时,或当您使用 VISIBLEINVISIBLEGONE 调用视图的 setVisibility() 方法时,这些视图可能会经历出现和消失动画。ViewGroup 中的其余 View 也可以 添加或移除 View 时,以动画方式移动到新位置。你可以定义 LayoutTransition 对象中的以下动画 通过调用 setAnimator() 并传入 Animator 对象,其中 以下 LayoutTransition 常量:

  • APPEARING - 该标志表示动画在 容器中显示的内容
  • CHANGE_APPEARING - 该标志表示动画在 会由于容器中出现新项而发生变化。
  • DISAPPEARING - 该标记表示动画在从容器中消失的项上运行。
  • CHANGE_DISAPPEARING - 该标记表示动画在因某个项从容器中消失而变化的项上运行。

您可以为这四类事件定义您自己的自定义动画,从而自定义布局转换的外观,或者告诉动画系统使用默认动画。

如需将 ViewGroup 的 android:animateLayoutchanges 属性设置为 true,请执行以下操作:

<LinearLayout
    android:orientation="vertical"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:id="@+id/verticalContainer"
    android:animateLayoutChanges="true" />

将此属性设为 true 可自动为添加到此或从中移除的 View 添加动画效果 ViewGroup 以及 ViewGroup 中的其他 View。

使用 StateListAnimator 为视图状态更改添加动画效果

通过 StateListAnimator 类,您可以定义在视图状态更改时运行的 Animator。此对象充当 Animator 对象的封装容器,只要指定的视图状态(例如“按下”或“聚焦”)发生更改,就会调用该动画。

可使用根 <selector> 元素和子 <item> 元素在 XML 资源中定义 StateListAnimator,每个元素都指定一个由 StateListAnimator 类定义的不同视图状态。每个 <item> 都包含一个属性动画集的定义。

例如,以下文件会创建一个状态列表 animator,用于更改 x 和 y 缩放 视图的以下按钮:

res/xml/animate_scale.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- the pressed state; increase x and y size to 150% -->
    <item android:state_pressed="true">
        <set>
            <objectAnimator android:propertyName="scaleX"
                android:duration="@android:integer/config_shortAnimTime"
                android:valueTo="1.5"
                android:valueType="floatType"/>
            <objectAnimator android:propertyName="scaleY"
                android:duration="@android:integer/config_shortAnimTime"
                android:valueTo="1.5"
                android:valueType="floatType"/>
        </set>
    </item>
    <!-- the default, non-pressed state; set x and y size to 100% -->
    <item android:state_pressed="false">
        <set>
            <objectAnimator android:propertyName="scaleX"
                android:duration="@android:integer/config_shortAnimTime"
                android:valueTo="1"
                android:valueType="floatType"/>
            <objectAnimator android:propertyName="scaleY"
                android:duration="@android:integer/config_shortAnimTime"
                android:valueTo="1"
                android:valueType="floatType"/>
        </set>
    </item>
</selector>

如需将状态列表动画师附加到视图,请添加 android:stateListAnimator 属性,如下所示:

<Button android:stateListAnimator="@xml/animate_scale"
        ... />

现在,当此按钮的状态发生变化时,将使用 animate_scale.xml 中定义的动画 状态更改

或者,如需将状态列表 Animator 分配给代码中的视图,请使用 AnimatorInflater.loadStateListAnimator() 方法,并将 Animator 分配给 使用 View.setStateListAnimator() 方法构建视图。

您还可以使用 AnimatedStateListDrawable 在状态更改间播放可绘制动画,而不是为视图的属性添加动画效果。其中一些系统小组件 Android 5.0 默认使用这些动画。以下示例展示了如何 将 AnimatedStateListDrawable 定义为 XML 资源:

<!-- res/drawable/myanimstatedrawable.xml -->
<animated-selector
    xmlns:android="http://schemas.android.com/apk/res/android">

    <!-- provide a different drawable for each state-->
    <item android:id="@+id/pressed" android:drawable="@drawable/drawableP"
        android:state_pressed="true"/>
    <item android:id="@+id/focused" android:drawable="@drawable/drawableF"
        android:state_focused="true"/>
    <item android:id="@id/default"
        android:drawable="@drawable/drawableD"/>

    <!-- specify a transition -->
    <transition android:fromId="@+id/default" android:toId="@+id/pressed">
        <animation-list>
            <item android:duration="15" android:drawable="@drawable/dt1"/>
            <item android:duration="15" android:drawable="@drawable/dt2"/>
            ...
        </animation-list>
    </transition>
    ...
</animated-selector>

使用 TypeEvaluator

如果要为 Android 系统无法识别的类型添加动画效果,则可以通过实现 TypeEvaluator 接口来创建您自己的评估程序。使系统能够 是 intfloat 或颜色,它们是 受 IntEvaluatorFloatEvaluatorArgbEvaluator 类型支持 评估程序。

TypeEvaluator 接口中只有一种要实现的方法,那就是 evaluate() 方法。这样, 您用来在 动画的当前点。FloatEvaluator 类演示了 操作方法:

Kotlin

private class FloatEvaluator : TypeEvaluator<Any> {

    override fun evaluate(fraction: Float, startValue: Any, endValue: Any): Any {
        return (startValue as Number).toFloat().let { startFloat ->
            startFloat + fraction * ((endValue as Number).toFloat() - startFloat)
        }
    }

}

Java

public class FloatEvaluator implements TypeEvaluator {

    public Object evaluate(float fraction, Object startValue, Object endValue) {
        float startFloat = ((Number) startValue).floatValue();
        return startFloat + fraction * (((Number) endValue).floatValue() - startFloat);
    }
}

注意:当 ValueAnimator(或 ObjectAnimator)运行时,它会计算动画当前的已完成动画分数(一个介于 0 和 1 之间的值),然后根据您正在使用的插值器来计算该分数的插值版本。插值分数是 TypeEvaluator 通过 fraction 参数接收的,因此您在计算添加动画效果之后的值时无需考虑插值器。

使用插值器

插值器定义了如何根据 。例如,您可以指定动画在整个动画过程中线性发生,这意味着动画在整个时间内均匀移动;也可以指定动画使用非线性时间,例如在动画的开始或结束处使用加速或减速。

动画系统中的插值器会从 Animator 接收代表 动画的已播放时长。插值器会修改此分数,使其与要提供的动画类型保持一致。Android 系统在 android.view.animation package 中提供了一组常用的插值器。如果这些都不适合 您可以实现 TimeInterpolator 接口并创建您自己的 。

例如,下面对默认插值器 AccelerateDecelerateInterpolatorLinearInterpolator 计算插值分数的方式进行了比较。 LinearInterpolator 对已完成动画分数没有任何影响。AccelerateDecelerateInterpolator 会在动画开始后加速,并在动画结束前减速。以下方法定义了这些插值器的逻辑:

AccelerateDecelerateInterpolator

Kotlin

override fun getInterpolation(input: Float): Float =
        (Math.cos((input + 1) * Math.PI) / 2.0f).toFloat() + 0.5f

Java

@Override
public float getInterpolation(float input) {
    return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
}

LinearInterpolator

Kotlin

override fun getInterpolation(input: Float): Float = input

Java

@Override
public float getInterpolation(float input) {
    return input;
}

下表显示了通过上述方法计算得出的近似值 用于时长 1000 毫秒的动画的插值器:

已完成毫秒数 已完成动画分数/插值分数(线性) 插值分数(加速/减速)
0 0 0
200 0.2 0.1
400 0.4 0.345
600 0.6 .654
800 0.8 0.9
1000 1 1

如上表所示,LinearInterpolator 以相同的速度更改值,每 200ms 变化 0.2。AccelerateDecelerateInterpolator 在 200 毫秒到 600 毫秒之间更改值的速度比 LinearInterpolator 快,在 600 毫秒到 600 毫秒之间变化较慢 1000 毫秒。

指定关键帧

Keyframe 对象由时间值对组成,用于在动画的特定时间定义特定的状态。每个关键帧还可以用自己的插值器控制动画在上一关键帧时间和此关键帧时间之间的时间间隔内的行为。

如需实例化 Keyframe 对象,您必须使用它的任一工厂方法(ofInt()ofFloat()ofObject())来获取类型合适的 Keyframe。然后,通过调用 ofKeyframe() 工厂方法来获取 PropertyValuesHolder 对象。获得对象后,您可以 通过传入 PropertyValuesHolder 对象获取 Animator 添加动画效果以下代码段演示了如何做到这一点:

Kotlin

val kf0 = Keyframe.ofFloat(0f, 0f)
val kf1 = Keyframe.ofFloat(.5f, 360f)
val kf2 = Keyframe.ofFloat(1f, 0f)
val pvhRotation = PropertyValuesHolder.ofKeyframe("rotation", kf0, kf1, kf2)
ObjectAnimator.ofPropertyValuesHolder(target, pvhRotation).apply {
    duration = 5000
}

Java

Keyframe kf0 = Keyframe.ofFloat(0f, 0f);
Keyframe kf1 = Keyframe.ofFloat(.5f, 360f);
Keyframe kf2 = Keyframe.ofFloat(1f, 0f);
PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofKeyframe("rotation", kf0, kf1, kf2);
ObjectAnimator rotationAnim = ObjectAnimator.ofPropertyValuesHolder(target, pvhRotation);
rotationAnim.setDuration(5000);

为视图添加动画效果

属性动画系统支持为视图对象添加简化的动画, 相较于视图动画系统的一些优势。视图 动画系统通过更改视图对象的绘制方式来转换这些对象。这是在每个视图的容器中处理的,因为视图本身没有可操控的属性。这会导致视图在表面上添加了动画效果,但视图对象本身没有任何变化。这会产生不好的效果,例如,某个对象已经在屏幕的其他位置绘制,但它仍位于其原始位置。在 Android 3.0 中,我们添加了新的属性以及相应的 getter 和 setter 方法来消除此缺陷。

属性动画系统可以通过更改视图对象中的实际属性来为屏幕上的视图添加动画效果。在 此外,View 还会自动调用 invalidate() 方法,以便在其属性发生更改时刷新屏幕。View 类中有利于属性动画的新属性包括:

  • translationXtranslationY:这些属性用于控制 视图位于其左侧和顶部坐标的增量内,后者由其布局设置 容器。
  • rotationrotationXrotationY:这些属性用于控制视图围绕轴心点进行的 2D(rotation 属性)和 3D 旋转。
  • scaleXscaleY:这些属性用于控制视图围绕其轴心点进行的 2D 缩放。
  • pivotXpivotY:这些属性用于控制旋转和缩放转换所围绕的轴心点的位置。默认情况下,数据透视 点位于对象的中心。
  • xy:这些是简单的实用属性,用于描述视图在容器中的最终位置,值分别为左侧值与 translationX 值的和以及顶部值与 translationY 值的和。
  • alpha:表示视图的 Alpha 透明度。此值默认为 1(不透明),值为 0 则表示完全透明(不可见)。

如需为 View 对象的属性(例如其颜色或旋转值)添加动画效果,您只需创建一个属性 Animator 并指定要添加动画效果的 View 属性即可。例如:

Kotlin

ObjectAnimator.ofFloat(myView, "rotation", 0f, 360f)

Java

ObjectAnimator.ofFloat(myView, "rotation", 0f, 360f);

如需详细了解如何创建动画,请参阅有关使用 ValueAnimatorObjectAnimator

使用 ViewPropertyAnimator 添加动画效果

ViewPropertyAnimator 有助于使用单个底层 Animator 对象轻松为 View 的多个属性并行添加动画效果。它的行为与 ObjectAnimator 非常相似,因为它会修改 是视图属性的实际值,但在为多个属性添加动画效果时, 一次。此外,使用 ViewPropertyAnimator 的代码更加简洁,也更易读。以下代码段展示了在同时为视图的 xy 属性添加动画效果时,使用多个 ObjectAnimator 对象、使用单个 ObjectAnimator 对象以及使用 ViewPropertyAnimator 的区别。

多个 ObjectAnimator 对象

Kotlin

val animX = ObjectAnimator.ofFloat(myView, "x", 50f)
val animY = ObjectAnimator.ofFloat(myView, "y", 100f)
AnimatorSet().apply {
    playTogether(animX, animY)
    start()
}

Java

ObjectAnimator animX = ObjectAnimator.ofFloat(myView, "x", 50f);
ObjectAnimator animY = ObjectAnimator.ofFloat(myView, "y", 100f);
AnimatorSet animSetXY = new AnimatorSet();
animSetXY.playTogether(animX, animY);
animSetXY.start();

一个 ObjectAnimator

Kotlin

val pvhX = PropertyValuesHolder.ofFloat("x", 50f)
val pvhY = PropertyValuesHolder.ofFloat("y", 100f)
ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvhY).start()

Java

PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("x", 50f);
PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("y", 100f);
ObjectAnimator.ofPropertyValuesHolder(myView, pvhX, pvhY).start();

ViewPropertyAnimator

Kotlin

myView.animate().x(50f).y(100f)

Java

myView.animate().x(50f).y(100f);

如需详细了解 ViewPropertyAnimator,请参阅相应的 Android 开发者博文

在 XML 中声明动画

通过属性动画系统,您可以使用 XML 来声明属性动画, 以编程方式创建通过在 XML 中定义动画,您可以轻松地在多个 activity 中重复使用动画,还能更轻松地修改动画序列。

为了区分使用新的属性动画 API 的动画文件与使用 旧版视图动画框架 从 Android 3.1 开始,您应将属性动画的 XML 文件保存在 res/animator/ 目录中。

以下属性动画类具有带相应 XML 标签的 XML 声明支持:

要查找可在 XML 声明中使用的属性,请参见动画 资源。以下示例依次播放两组对象动画,其中第一个嵌套集会同时播放两个对象动画:

<set android:ordering="sequentially">
    <set>
        <objectAnimator
            android:propertyName="x"
            android:duration="500"
            android:valueTo="400"
            android:valueType="intType"/>
        <objectAnimator
            android:propertyName="y"
            android:duration="500"
            android:valueTo="300"
            android:valueType="intType"/>
    </set>
    <objectAnimator
        android:propertyName="alpha"
        android:duration="500"
        android:valueTo="1f"/>
</set>

为了运行此动画,您必须将代码中的 XML 资源扩充为 AnimatorSet 对象,然后在开始运行动画集之前为所有动画设置目标对象。为方便起见,调用 setTarget() 即可设置一个用于 AnimatorSet 的所有子项的目标对象。以下代码展示了如何执行此操作:

Kotlin

(AnimatorInflater.loadAnimator(myContext, R.animator.property_animator) as AnimatorSet).apply {
    setTarget(myObject)
    start()
}

Java

AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(myContext,
    R.animator.property_animator);
set.setTarget(myObject);
set.start();

您还可以在 XML 中声明 ValueAnimator,如以下示例所示:

<animator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="1000"
    android:valueType="floatType"
    android:valueFrom="0f"
    android:valueTo="-100f" />

如需在代码中使用上一个 ValueAnimator,您必须膨胀对象、添加 AnimatorUpdateListener、获取更新后的动画值,并在某个视图的属性中使用它,如下面的代码所示:

Kotlin

(AnimatorInflater.loadAnimator(this, R.animator.animator) as ValueAnimator).apply {
    addUpdateListener { updatedAnimation ->
        textView.translationX = updatedAnimation.animatedValue as Float
    }

    start()
}

Java

ValueAnimator xmlAnimator = (ValueAnimator) AnimatorInflater.loadAnimator(this,
        R.animator.animator);
xmlAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator updatedAnimation) {
        float animatedValue = (float)updatedAnimation.getAnimatedValue();
        textView.setTranslationX(animatedValue);
    }
});

xmlAnimator.start();

有关定义属性动画的 XML 语法的信息,请参见动画 资源

对界面性能的潜在影响

更新界面的 Animator 会导致 动画的运行时间因此,使用资源密集型动画可能会对应用的性能产生负面影响。

为界面添加动画效果所需的工作已添加到渲染管道的动画阶段。您可以查看动画是否会影响 通过启用 GPU 渲染模式分析和 监控动画阶段。如需了解详情,请参阅 GPU 渲染模式分析 演示