属性动画概览

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

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

通过属性动画系统,您可以定义 动画:

  • 时长:您可以指定动画的时长。默认时长为 300 毫秒。
  • 时间插值:您可以指定如何以 函数。
  • 重复计数和行为:您可以指定在发生以下情况时是否重复播放动画: 播放动画。您还可以 指定是否要反向播放动画。将其设置为反向播放 动画先快进再反复播放,直到达到重复次数。
  • Animator 集:您可以将动画分组到一起播放或 或在指定延迟时间之后。
  • 帧刷新延迟:您可以指定动画帧的刷新频率。通过 默认设置为每 10 毫秒刷新一次,但应用的帧刷新速度为 最终取决于系统的整体繁忙程度以及系统为底层计时器提供服务的速度。

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

属性动画的工作原理

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

图 1. 线性动画示例

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

图 2. 非线性动画示例

我们来详细了解一下属性动画系统的重要组件是如何 会计算如上图所示的动画。图 3 说明了主类 相互协作。

图 3. 如何计算动画

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

ValueAnimator 封装了 TimeInterpolator(用于定义动画插值)和 TypeEvaluator(用于定义如何计算所具有的属性的值) 动画效果。例如,在图 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 = 时的插值分数为 0 .15。 10 ms,因此该属性的值将是 0.15 × (40 - 0) 或 6。

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

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

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

借助属性动画系统,这些约束条件会被完全消除,并且您可以为 任何对象(View 和非 View)的任何属性,并且对象本身实际上已经过修改。 属性动画系统在执行动画方面也更为强健。在 大体上讲,您可以为要添加动画效果的属性分配动画程序,例如颜色、 位置或大小,并可以定义动画的各个方面,例如插值和 同步多个 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 或 color 来设定动画的时长 添加动画效果您可以通过调用以下方法之一来获取 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();

在此代码中,ValueAnimator 开始计算 0 到 100 之间的动画时长,时长为 1000 毫秒(当 start() 方法运行时)。

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

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 毫秒时长的逻辑。

您可以通过添加 AnimatorUpdateListener 添加到 ValueAnimator 对象,如 以下代码:

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 添加动画效果

ObjectAnimatorValueAnimator 的子类(上一部分已讨论过),它结合了时间设置 ValueAnimator 的引擎和价值计算,能够 为目标对象的命名属性添加动画效果。这大大简化了为任何对象添加动画效果的过程, 不再需要实现 ValueAnimator.AnimatorUpdateListener, 因为动画属性会自动更新。

实例化 ObjectAnimator 的操作与 ValueAnimator 类似,但您还需要指定对象以及该对象属性的名称(如 字符串),以及要在其间添加动画效果的值:

Kotlin

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

Java

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

ObjectAnimator 更新属性 您必须执行以下操作:

  • 要添加动画效果的对象属性必须具有 setter 函数(采用驼峰式大小写形式),格式为 set<PropertyName>()。因为 ObjectAnimator 在动画播放期间自动更新属性,它必须能够访问该属性 使用此 setter 方法。例如,如果属性名称为 foo,则需要执行以下操作: 具有 setFoo() 方法。如果此 setter 方法不存在,您将得到三个 选项: <ph type="x-smartling-placeholder">
      </ph>
    • 如果您有权限,可将 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 属性添加动画效果只会导致系统对 重新绘制自身时返回屏幕。View 上的所有属性 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();

动画监听器

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

您可以扩展 AnimatorListenerAdapter 类,而不是 实现 Animator.AnimatorListener 接口(如果没有 希望实现 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 内的视图 这些动画会逐一出现和消失动画 或者当您调用 View 的 setVisibility() 方法与 VISIBLEINVISIBLEGONE。ViewGroup 中的其余 View 也可以 添加或移除 View 时,以动画形式移动到新位置。你可以定义 LayoutTransition 对象中的以下动画 通过调用 setAnimator() 并传入包含其中一个参数的 Animator 对象, 以下 LayoutTransition 常量:

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

您可以为这四种类型的事件定义自己的自定义动画,以自定义外观 或者直接指示动画系统使用默认动画。

若要将 android:animateLayoutchanges 属性设置为 true, ViewGroup 将执行以下操作:

<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 对象,每当指定 视图状态(例如“按下”或“聚焦”)的变化。

可以使用根目录在 XML 资源中定义 StateListAnimator <selector> 元素和 <item> 子元素(各自指定) 由 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>

要将状态列表 Animator 附加到视图,请添加 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 0.8
800 0.8 0.9
1000 1 1

如上表所示,LinearInterpolator 会更改 以相同的速度传递,每传递 200 毫秒返回 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);

为视图添加动画效果

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

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

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

要为 View 对象的属性(例如其颜色或旋转值)添加动画效果,只需 方法是创建一个属性动画程序,并指定要 设置动画效果。例如:

Kotlin

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

Java

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

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

使用 ViewPropertyAnimator 添加动画效果

ViewPropertyAnimator 提供了一种简单的方法来为多个 View 属性并行使用单个底层Animator 对象。它的行为与 ObjectAnimator 非常相似,因为它会修改 使用视图属性的实际值,但在为多个属性添加动画效果时, 一次。此外,使用 ViewPropertyAnimator 的代码是 更简洁、更易读。以下代码段显示了使用 ObjectAnimator 对象,单个 ObjectAnimator,且ViewPropertyAnimator 同时为视图的 xy 属性添加动画效果。

多个 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 中定义动画,你可以轻松地重复使用动画 并且可以更轻松地修改动画序列。

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

以下属性动画类支持带有 以下 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 渲染模式分析 演示