【Android】Android 的三种动画(帧动画、View 动画、属性动画)

【Android】Android 的三种动画(帧动画、View 动画、属性动画)

Android 的动画可以分为三种:View 动画、帧动画和属性动画,其中帧动画也属于 View 动画的一种,只不过它和平移、旋转等常见的 View 动画在表现形式上略有不同。View 动画通过对场景里的对象不断做图像变换(平移、缩放、旋转、透明度)从而产生动画效果,它是一种渐进式动画,并且 View 动画支持自定义。帧动画通过顺序播放一系列图像从而产生动画效果,可以简单理解为图片切换动画,如果图片过多过大就会导致 OOM。属性动画通过动态地改变对象的属性从而达到动画效果。

帧动画

帧动画是通过连续播放一系列图像来形成动画,类似于GIF。

帧动画的使用比较简单,首先需要通过 XML 来定义一个 AnimationDrawable, 如下所示:

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="false">
    <item android:drawable="@drawable/image1" android:duration="800"/>
    <item android:drawable="@drawable/image2" android:duration="800"/>
    <item android:drawable="@drawable/image3" android:duration="800"/>

</animation-list>

然后将上述的 Drawable 作为 View 的背景并通过 Drawable 来播放即可:

xml 复制代码
<ImageView
    android:id="@+id/image_view"
    android:layout_width="160dp"
    android:layout_height="160dp"
    android:layout_gravity="center"
    android:src="@drawable/frame_animation"
    android:scaleType="centerCrop" />
java 复制代码
ImageView imageView = findViewById(R.id.image_view);
AnimationDrawable drawable = (AnimationDrawable) imageView.getDrawable();
drawable.start();

效果如下:

帧动画的使用比较简单,但是比较容易引起 OOM(内存溢出),所以在使用帧动画时应尽量避免使用过多尺寸较大的图片。

View 动画(补间动画)

View动画是一种在视图上执行简单变换(如平移、旋转、缩放和透明度变化)的动画系统。它也被称为补间动画,因为它在视图的起始状态和结束状态之间生成中间帧。我们只需要拿到一个view,设定它开始和结束的位置,中间的view会自动由系统补齐,而不需要帧动画每一幅图都是提前准备好的。

View动画的四种基本效果对应了四个Animation的子类,如下:

要使用 View 动画,首先要在res/anim创建动画的 XML 文件。View 动画的描述文件基本结构如下:

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:shareInterpolator="true|false"
    android:duration="整数毫秒值"
    android:fillAfter="true|false"
    android:fillBefore="true|false"
    android:repeatCount="整数|infinite"
    android:repeatMode="restart|reverse"
    android:interpolator="@android:anim/插值器资源">
    
    <!-- 包含的动画元素 -->
    <alpha/>
    <scale/>
    <rotate/>
    <translate/>
    
</set>

四种基本动画类型

1. 透明度动画 (Alpha)
xml 复制代码
<alpha
    android:fromAlpha="0.0-1.0"
    android:toAlpha="0.0-1.0"
    android:duration="毫秒"
    android:startOffset="延迟毫秒"
    android:repeatCount="整数|infinite"
    android:repeatMode="restart|reverse"/>

示例:淡入效果

xml 复制代码
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromAlpha="0.0"
    android:toAlpha="1.0"
    android:duration="1000"/>
  • android:fromAlpha="0.0":起始透明度,完全透明
  • android:toAlpha="1.0":结束透明度,完全不透明
  • android:duration="1000":动画持续 1000 毫秒

效果 :控件在 1 秒内,从透明逐渐变为可见 ,也就是常见的 淡入效果

2. 缩放动画 (Scale)
xml 复制代码
<scale
    android:fromXScale="起始X缩放"
    android:toXScale="结束X缩放"
    android:fromYScale="起始Y缩放"
    android:toYScale="结束Y缩放"
    android:pivotX="X轴基准点"
    android:pivotY="Y轴基准点"
    android:duration="毫秒"/>

基准点取值

  • 50%:相对于自身中心
  • 50%p:相对于父容器中心
  • 数值:具体像素值

示例:中心放大效果

xml 复制代码
<scale xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromXScale="0.5"
    android:toXScale="1.0"
    android:fromYScale="0.5"
    android:toYScale="1.0"
    android:pivotX="50%"
    android:pivotY="50%"
    android:duration="600"/>
  • android:fromXScale="0.5":X 方向初始缩放比例为 0.5(即宽度一半)。
  • android:toXScale="1.0":X 方向最终缩放比例为 1.0(即恢复正常宽度)。
  • android:fromYScale="0.5":Y 方向初始缩放比例为 0.5(即高度一半)。
  • android:toYScale="1.0":Y 方向最终缩放比例为 1.0
  • android:pivotX="50%":缩放的中心点 X 在控件宽度的 50%(中心位置)。
  • android:pivotY="50%":缩放的中心点 Y 在控件高度的 50%(中心位置)。
  • android:duration="600":动画持续 600 毫秒

效果 :控件从一半大小逐渐放大到正常大小,类似 "弹出效果"

3. 旋转动画 (Rotate)
xml 复制代码
<rotate
    android:fromDegrees="起始角度"
    android:toDegrees="结束角度"
    android:pivotX="X轴旋转中心"
    android:pivotY="Y轴旋转中心"
    android:duration="毫秒"/>

示例:无限旋转

xml 复制代码
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromDegrees="0"
    android:toDegrees="360"
    android:pivotX="50%"
    android:pivotY="50%"
    android:repeatCount="infinite"
    android:duration="1000"/>
  • android:fromDegrees="0":动画开始时的角度,
  • android:toDegrees="360":动画结束时的角度,360°,即完整旋转一圈。
  • android:pivotX="50%":旋转中心点 X 轴在控件宽度的 50%(居中)。
  • android:pivotY="50%":旋转中心点 Y 轴在控件高度的 50%(居中)。
  • android:repeatCount="infinite":动画无限循环。
  • android:duration="1000":动画时长 1000ms(1秒),即每秒旋转一圈。

效果 :控件会围绕自身中心点 不断顺时针旋转

4. 平移动画 (Translate)
xml 复制代码
<translate
    android:fromXDelta="起始X位置"
    android:toXDelta="结束X位置"
    android:fromYDelta="起始Y位置"
    android:toYDelta="结束Y位置"
    android:duration="毫秒"/>

位置取值

  • 数值:具体像素值
  • n%:相对于自身宽高的百分比
  • n%p:相对于父容器宽高的百分比

示例:从右侧滑入

xml 复制代码
<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromXDelta="100%p"
    android:toXDelta="0"
    android:duration="400"/>
  • android:fromXDelta="100%p":动画开始时,X 方向位置相对父控件宽度的 100%,也就是在屏幕外右侧。
  • android:toXDelta="0":动画结束时,X 方向位移为 0,也就是回到原位置。
  • android:duration="400":动画持续时间为 400 毫秒

效果:让一个视图从屏幕右侧滑入到原本的位置。

组合动画 (Set)

xml 复制代码
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:ordering="together|sequentially">
    
    <!-- 包含多个动画元素 -->
    <alpha .../>
    <scale .../>
    <rotate .../>
    
</set>

ordering属性

  • together(默认):所有动画同时执行
  • sequentially:动画按顺序执行

示例:组合动画(淡入+放大)

xml 复制代码
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:shareInterpolator="true"
    android:duration="600">
    
    <alpha
        android:fromAlpha="0.0"
        android:toAlpha="1.0"/>
        
    <scale
        android:fromXScale="0.5"
        android:toXScale="1.0"
        android:fromYScale="0.5"
        android:toYScale="1.0"
        android:pivotX="50%"
        android:pivotY="50%"/>
        
</set>
  • <set>:表示一个动画集合,里面可以放多个 <alpha>, <scale>, <translate>, <rotate> 等动画。
  • android:shareInterpolator="true":表示集合里所有动画共享同一个插值器(默认是加速-减速插值)。
  • android:duration="600":默认持续时间 600 毫秒,作用于所有子动画(除非子动画自己定义了 duration)。

子动画:

  1. alpha :从完全透明 0.0 → 完全不透明 1.0(淡入)。
  2. scale :从 0.5 倍大小 → 1.0 正常大小(中心点在控件的 50%,即正中间)。

效果 :控件在 600ms 内,从小且透明 → 放大并逐渐显示,就像"弹出淡入"的效果。

常用属性详解

  • duration:动画持续时间(毫秒)

  • startOffset:动画开始前的延迟时间(毫秒)

  • repeatCount:重复次数(整数或" infinite ")

  • repeatModerestart:从头开始重复(默认)reverse:反向重复

  • fillAfter:动画结束后保持结束状态

  • fillBefore:动画开始前应用初始状态(默认true)

  • fillEnabled:启用 fillBefore / fillAfter

插值器 (Interpolator)

控制动画速度变化:

xml 复制代码
android:interpolator="@android:anim/插值器资源"
  • android:shareInterpolator:表示集合中的动画是否和集合共享同一个插值器。如果集合不指定插值器,那么子动画就需要单独指定所需的插值器或者使用默认值。

常用系统插值器

  • @android:anim/linear_interpolator:匀速
  • @android:anim/accelerate_interpolator:加速
  • @android:anim/decelerate_interpolator:减速
  • @android:anim/accelerate_decelerate_interpolator:先加速后减速
  • @android:anim/bounce_interpolator:弹跳效果
  • @android:anim/anticipate_interpolator:先回拉再前进
  • @android:anim/overshoot_interpolator:超出终点再返回
  • @android:anim/anticipate_overshoot_interpolator:先回拉再超出

完整示例

1. 按钮点击效果(缩放)
xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <scale
        android:fromXScale="1.0"
        android:toXScale="0.9"
        android:fromYScale="1.0"
        android:toYScale="0.9"
        android:pivotX="50%"
        android:pivotY="50%"
        android:duration="100"/>

    <scale
        android:fromXScale="0.9"
        android:toXScale="1.0"
        android:fromYScale="0.9"
        android:toYScale="1.0"
        android:pivotX="50%"
        android:pivotY="50%"
        android:startOffset="100"
        android:duration="100"/>
</set>
  1. 第一个 <scale>
    • fromXScale="1.0"toXScale="0.9"
    • fromYScale="1.0"toYScale="0.9"
    • 100 毫秒 内,控件从正常大小缩小到 90%。
    • 看起来就像"按下去"的效果。
  2. 第二个 <scale>
    • fromXScale="0.9"toXScale="1.0"
    • fromYScale="0.9"toYScale="1.0"
    • android:startOffset="100",所以它会在第一个动画执行完后再开始。
    • 100 毫秒 内,把控件从缩小的 90% 恢复到正常大小。
    • 看起来像"弹回去"。

总体效果 :控件 先缩小一下,再恢复 ,类似点击按钮时的 按压反馈动效

2. 列表项入场动画(顺序显示)
xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:ordering="sequentially"
    android:duration="400">

    <translate
        android:fromYDelta="20%"
        android:toYDelta="0"
        android:interpolator="@android:anim/decelerate_interpolator"/>

    <alpha
        android:fromAlpha="0"
        android:toAlpha="1"
        android:interpolator="@android:anim/accelerate_interpolator"/>
</set>
  • android:ordering="sequentially":表示子动画 按顺序执行(而不是同时执行)。
  • android:duration="400":默认持续时间为 400ms(每个子动画如果没单独设置,会用这个时间)。

子动画:

  1. translate
    • fromYDelta="20%"toYDelta="0"
    • 控件从自身高度的 20% 下方 位置,向上移动到原位。
    • @android:anim/decelerate_interpolator:减速插值器,开始快 → 结束慢。
    • 效果:控件 向上滑入
  2. alpha
    • fromAlpha="0"toAlpha="1"
    • 控件从完全透明 → 完全可见。
    • @android:anim/accelerate_interpolator:加速插值器,开始慢 → 结束快。
    • 效果:控件 淡入显示

整体效果:控件先从下方滑入,然后再淡入显示。

3. 页面切换动画(滑入滑出)
xml 复制代码
<!-- res/anim/slide_in_left.xml -->
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:fromXDelta="-50%p"
        android:toXDelta="0"
        android:duration="300"/>
    <alpha
        android:fromAlpha="0.5"
        android:toAlpha="1"
        android:duration="300"/>
</set>

<!-- res/anim/slide_out_right.xml -->
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:fromXDelta="0"
        android:toXDelta="100%p"
        android:duration="300"/>
    <alpha
        android:fromAlpha="1"
        android:toAlpha="0.5"
        android:duration="300"/>
</set>

slide_in_left.xml

  • translate:从左边 -50% 宽度的位置滑动到 0(原位)。
  • alpha:从半透明(0.5)变为完全不透明(1.0)。
  • 效果:一个控件会从左边滑入,并逐渐清晰。

slide_out_right.xml

  • translate:从原位移动到右边屏幕外(100%)。
  • alpha:从不透明(1.0)变为半透明(0.5)。
  • 效果:一个控件会向右滑出,同时逐渐消失。

在代码中加载动画

java 复制代码
// 加载动画资源
Animation animation = AnimationUtils.loadAnimation(context, R.anim.your_animation);

// 应用动画
View view = findViewById(R.id.your_view);
view.startAnimation(animation);

// 设置动画监听
animation.setAnimationListener(new Animation.AnimationListener() {
    @Override
    public void onAnimationStart(Animation animation) {
        // 动画开始
    }
    
    @Override
    public void onAnimationEnd(Animation animation) {
        // 动画结束
    }
    
    @Override
    public void onAnimationRepeat(Animation animation) {
        // 动画重复
    }
});

View 动画的特殊使用场景

除了上面四种形式之外,View 动画还可以在一些特殊的场景下使用,比如在 ViewGroup 种可以控制子元素的出场效果,在 Activity 种可以实现不同 Activity 之间的切换效果。

1. LayoutAnimation

LayoutAnimation 作用于 ViewGroup,为 ViewGroup 指定一个动画,这样当它的子元素出场时都会具有这种动画效果。LayoutAnimation 也是一个 View 动画,为了给 ViewGroup 的子元素加上出场效果,遵循如下几个步骤:

(1)定义 LayoutAnimation 控制器(指定每个子项播放顺序和间隔)

如下所示:

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android"
    android:delay="0.15"
    android:animationOrder="normal"
    android:animation="@anim/item_anim" />

android:delay

指定每个子 View 动画开始的间隔时间(相对于动画时长的比例)比如:

  • 动画时长是 400ms
  • delay = 0.15; → 第一个子 View 立即播放; → 第二个子 View 延迟 0.15 × 400 = 60ms; → 第三个延迟 120ms,以此类推。

android:animationOrder

控制子 View 动画的播放顺序:

  • normal:从第一个子 View 开始,按顺序播放(常用)
  • reverse:从最后一个子 View 开始,倒序播放
  • random:随机顺序播放(每个子项随机延迟)
(2)定义单个 item 的动画(滑入 + 淡入)
xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="400">
    <translate
        android:fromYDelta="30%"
        android:toYDelta="0" />

    <alpha
        android:fromAlpha="0.0"
        android:toAlpha="1.0" />
</set>
(3)为 ViewGroup 指定 layoutAnimation属性

例如:

xml 复制代码
<ListView
    android:id="@+id/listView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:dividerHeight="8dp"
    android:layoutAnimation="@anim/layout_animation" />

这样 ListView 的 item 就具有出场动画了,这种方式适用于所有的 ViewGroup。

除了在 XML 种指定 LayoutAnimation 外,还可以通过 LayoutAnimationController 来实现,具体代码如下所示:

java 复制代码
ListView listView = (ListView)findViewById(R.id.listView);
Animation animation = AnimationUtils.loadAnimation(this, R.anim.item_anim);
LayoutAnimationController controller = new LayoutAnimationController(animation);
controller.setDelay(0.15f);
controller.setOrder(LayoutAnimationController.ORDER.NORMAL);
listView.setLayoutAnimation(controller);

效果如下:

2. Activity 的切换效果

Activity 有默认的切换效果,但是这个效果我们是可以自定义的,主要用到 overridePendingTransition(int enterAnim, int exitAnim)这个方法,这个方法必须在 startActivity(Intent)或者finish()之后被调用才能生效,它的参数含义如下:

  • enterAnim:Activity 被打开时,所需的动画资源 id
  • exitAnim:Activity 被暂停时,所需的动画资源 id

当启动一个 Activity 时,可以按照如下方式为其添加自定义的切换效果:

java 复制代码
Intent intent = new Intent(this, TestActivity.class);
startActivity(intent);
overridePendingTransition(R.anim.enter_anim, R.animexit_anim);

当 Activity 退出时,也可以为其指定自己的切换效果,如下所示:

java 复制代码
@override
public void finish() {
    super.finish();
    overridePendingTransition(R.anim.enter_anim, R.anim.exit_anim);
}

需要注意的是,overridePendingTransition 这个方法必须位于 startActivity 或者 finish 后面,否则动画效果将不起作用。

属性动画

属性动画是Android 3.0(API 11)引入的强大动画框架,相比View动画,它能够实际改变对象的属性值,提供更灵活、更强大的动画能力。

在 Android 3.0 之前,动画系统主要依赖于 补间动画帧动画 。但它们有一个致命的缺陷:补间动画只是视觉上的变化,而非真正改变 View 的属性。例如,你让一个按钮从左边移动到右边(平移)。动画结束后,按钮看起来在右边,但你点击右边却没有反应,因为它的实际位置(left, top, right, bottom)还在原处。你只能点击原来的位置。这非常反直觉。属性动画 就是为了解决这个问题而诞生的。它的核心思想是:在指定的时间间隔内,通过不断地改变一个对象的真实属性的值,并同时刷新显示,来实现动画效果。

核心类介绍

  • ValueAnimation:数值动画器,驱动数值变化。
  • ObjectAnimation:对象动画器,自动更新对象属性。
  • AnimationSet:动画集合,管理多个动画的播放顺序和组合关系。
  • PropertyValuesHolder:属性值持有者,封装属性和对应的数值序列,实现一个动画改变多个属性。
  • Keyframe:关键帧,定义动画路径中的关键节点,实现复杂动画路径。

属性动画的基本使用

值动画(ValueAnimation)

ValueAnimation是属性动画最核心的类。它的职责很简单:计算动画过程中"值"的变化 。它本身不直接操作任何对象的属性,它只负责在设定的时间内,根据起始值和结束值,生成一系列连续的、过渡的"值"。可以把它理解为一个数值发生器

核心方法

java 复制代码
// 创建方法
ValueAnimator.ofInt(int... values)      // 整型数值动画
ValueAnimator.ofFloat(float... values)  // 浮点型数值动画
ValueAnimator.ofObject(TypeEvaluator evaluator, Object... values) // 自定义对象动画
ValueAnimator.ofArgb(int... values)     // 颜色值动画(API 21+)

// 关键配置
setDuration(long duration)              // 设置持续时间
setRepeatCount(int count)               // 设置重复次数
setRepeatMode(int mode)                 // 设置重复模式
setInterpolator(TimeInterpolator value) // 设置插值器

// 监听器
addUpdateListener(AnimatorUpdateListener listener) // 数值更新监听

使用流程

  1. 创建ValueAnimation,设置目标属性的起始值、结束值和持续时间。
  2. 添加更新监听器,在监听器中获取当前计算出的值。
  3. 手动将这个值赋值给目标对象的属性。

示例:让一个 TextView 上的数字从 1 平滑地变化到 100。

java 复制代码
// 1. 创建 ValueAnimator,从 1 到 100
ValueAnimator valueAnimator = ValueAnimator.ofInt(1, 100);
valueAnimator.setDuration(2000); // 动画持续 2 秒
valueAnimator.setInterpolator(new LinearInterpolator()); // 线性插值器:匀速动画

// 2. 监听每一帧的动画值变化
colorAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        int currentValue = (int) animation.getAnimatedValue(); // 当前的数值
        textView.setText(String.valueOf(currentValue)); // 手动更新 TextView
    }
});

// 3. 启动动画
colorAnim.start();
对象动画(ObjectAnimation)

ObjectAnimator类是 ValueAnimator 的子类,也是最常用的类。它在 ValueAnimator "计算值" 的基础上,增加了一个核心功能:自动将计算出的值设置给目标对象的指定属性。不需要手动去监听和赋值了。

核心方法

java 复制代码
ofFloat(Object target, String propertyName, float... values)
ofInt(Object target, String propertyName, int... values)  
ofObject(Object target, String propertyName, TypeEvaluator evaluator, Object... values)
ofArgb(Object target, String propertyName, int... values)

使用条件

要让 ObjectAnimator 正常工作,目标对象必须满足以下至少一个条件:

  1. 拥有该属性的 setter 方法:例如属性叫 foo,则需要有 setFoo() 方法。
  2. 使用包装器:如果对象没有 setter,你可以提供一个包装器,并通过它来设置值。
  3. 或者,你可以直接改变该属性的值(不推荐,违背封装原则)。

示例:将一个 View 在水平方向上平移 500px。

java 复制代码
// 参数:目标对象,属性名(字符串),起始值,结束值
ObjectAnimator anim = ObjectAnimator.ofFloat(myView, "translationX", 0f, 300f);
anim.setDuration(1000);
anim.start();

这段代码背后发生了什么?

  1. ObjectAnimator 会计算从 0f 到 500f 之间的一系列浮点数。
  2. 对于每一个计算出的值 value,它会去调用 myView.setTranslationX(value) 方法。
  3. View 的属性被改变,触发重绘,动画就产生了。

常用 View 属性:

  • translationX, translationY:平移
  • rotation, rotationX, rotationY:旋转
  • scaleX, scaleY:缩放
  • alpha:透明度
  • x, y: View 在父容器中的最终坐标(left + translationX
  • backgroundColor:背景颜色(需要 ArgbEvaluator 支持)
组合动画(AnimationSet)

用于将多个动画(ValueAnimator, ObjectAnimator 或其他 AnimatorSet)组合在一起播放,可以设置它们的先后顺序或同时播放。

核心方法

java 复制代码
// 播放控制
play(Animator anim)                     // 指定要播放的动画
with(Animator anim)                     // 与当前动画同时播放
before(Animator anim)                   // 在当前动画之前播放
after(Animator anim)                    // 在当前动画之后播放

// 便捷方法
playTogether(Animator... items)         // 同时播放多个动画
playSequentially(Animator... items)     // 顺序播放多个动画
playSequentially(List<Animator> items)  // 顺序播放动画列表

示例: 让一个 View 先平移,再旋转,最后淡出。

java 复制代码
ObjectAnimator moveIn = ObjectAnimator.ofFloat(myView, "translationX", -500f, 0f);
ObjectAnimator rotate = ObjectAnimator.ofFloat(myView, "rotation", 0f, 360f);
ObjectAnimator fadeOut = ObjectAnimator.ofFloat(myView, "alpha", 1f, 0f);

AnimatorSet animSet = new AnimatorSet();
// 按顺序执行
animSet.playSequentially(moveIn, rotate, fadeOut);
// 或者同时执行
// animSet.playTogether(moveIn, rotate, fadeOut);
// 或者更复杂的组合
// animSet.play(rotate).with(fadeOut).after(moveIn);

animSet.setDuration(1000);
animSet.start();

插值器与估值器

1. 插值器(Interpolator)
  • 作用 :定义了 动画变化的"节奏"或"速度曲线" 。它根据时间流逝的百分比 (elapsed fraction,0到1之间)计算出当前动画进度的百分比(interpolated fraction)。
  • 简单来说:输入一个线性变化的时间(比如过了总时间的50%),输出一个非线性的动画进度(比如实际上只完成了20%,或者已经完成了80%)。
  • 系统内置的插值器
    • LinearInterpolator:线性插值器,匀速动画
    • AccelerateInterpolator:加速插值器,动画越来越快
    • DecelerateInterpolator:减速插值器,动画越来越慢
    • AccelerateDecelerateInterpolator:先加速后减速(默认
    • BounceInterpolator:弹球效果
    • OvershootInterpolator:先快速完成动画,再回到结束样式
    • AnticipateInterpolator:先后退再加速前进

使用:

java 复制代码
animator.setInterpolator(new BounceInterpolator());
2. 类型估值器(TypeEvaluator)
  • 作用 :当动画的属性不是 int, floatcolor 这些基本类型时,或者你想对基本类型的变换规则进行自定义时,就需要它。它根据 当前动画进度的百分比(插值器计算出的)起始值、结束值 ,计算出当前帧的具体属性值
  • 简单来说:插值器输出"进度",估值器根据这个"进度"算出"当前值"。
  • 系统内置的估值器
    • IntEvaluator:用于计算 int 类型。
    • FloatEvaluator:用于计算 float 类型。
    • ArgbEvaluator:用于计算颜色值(非常重要!用于颜色过渡)。
    • RectEvaluator:用于计算 Rect 对象。

示例: 没有 ArgbEvaluator,颜色过渡会非常奇怪。

java 复制代码
// 正确使用 ArgbEvaluator 进行颜色过渡
ObjectAnimator colorAnim = ObjectAnimator.ofInt(myView, "backgroundColor", 0xFFFF0000, 0xFF0000FF);
colorAnim.setDuration(3000);
colorAnim.setEvaluator(new ArgbEvaluator()); // 关键!
colorAnim.start();

使用 XML 定义属性动画

与视图动画一样,属性动画也可以写在 res/animator/ 目录下的 XML 文件中,便于复用和预览。

XML 标签:

  • <animator>:对应 ValueAnimator
  • <objectAnimator>:对应 ObjectAnimator
  • <set>:对应 AnimatorSet

示例 (res/animator/scale_and_rotate.xml):

java 复制代码
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:ordering="sequentially"> <!-- 顺序执行 -->

    <objectAnimator
        android:propertyName="scaleX"
        android:duration="1000"
        android:valueFrom="1.0"
        android:valueTo="2.0"
        android:valueType="floatType"
        android:interpolator="@android:interpolator/accelerate_decelerate" />

    <objectAnimator
        android:propertyName="scaleY"
        android:duration="1000"
        android:valueFrom="1.0"
        android:valueTo="2.0"
        android:valueType="floatType"
        android:interpolator="@android:interpolator/accelerate_decelerate" />

    <objectAnimator
        android:propertyName="rotation"
        android:duration="1500"
        android:valueFrom="0"
        android:valueTo="360"
        android:valueType="floatType" />

</set>

在代码中加载:

java 复制代码
AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(context, R.animator.scale_and_rotate);
set.setTarget(myView); // 设置目标对象
set.start();

ViewPropertyAnimator - 简化多个属性同时动画

当需要同时改变一个 View 的多个属性时(如同时移动、旋转、透明),使用 View.animate() 方法会非常简洁高效。

传统方式 (AnimatorSet):

java 复制代码
ObjectAnimator animX = ObjectAnimator.ofFloat(myView, "translationX", 100f);
ObjectAnimator animY = ObjectAnimator.ofFloat(myView, "translationY", 100f);
AnimatorSet animSetXY = new AnimatorSet();
animSetXY.playTogether(animX, animY);
animSetXY.start();

简化方式 (ViewPropertyAnimator):

java 复制代码
myView.animate()
        .translationX(100f)
        .translationY(100f)
        .rotation(360f)
        .alpha(0.5f)
        .setDuration(1000)
        .start();

这种方式在性能和代码简洁性上都更优,是同时动画多个 View 属性时的首选

属性动画的监听器

通过 AnimatorListenerAnimatorUpdateListener 来监听动画的各种状态。

  • AnimatorListener:监听开始、结束、取消、重复。

    java 复制代码
    animator.addListener(new Animator.AnimatorListener() {
        @Override
        public void onAnimationStart(Animator animation) { }
        @Override
        public void onAnimationEnd(Animator animation) {
            // 动画结束,可以做清理工作或触发下一个动作
        }
        @Override
        public void onAnimationCancel(Animator animation) { }
        @Override
        public void onAnimationRepeat(Animator animation) { }
    });

    通常可以使用简化的 AnimatorListenerAdapter,只重写你需要的方法。

    java 复制代码
    animator.addListener(new AnimatorListenerAdapter() {
        @Override
        public void onAnimationEnd(Animator animation) {
            // 只处理结束事件
        }
    });
  • AnimatorUpdateListener:监听每一帧的更新(在 ValueAnimator 中已介绍)。

PropertyValuesHolder

PropertyValuesHolder(简称 PVH)是 ObjectAnimator 的"属性容器"。

普通的 ObjectAnimator 只能动画一个属性:

java 复制代码
ObjectAnimator.ofFloat(view, "alpha", 0f, 1f);

而通过 PropertyValuesHolder,你可以同时指定多个属性一起变化,比如:

java 复制代码
// 创建多个属性值持有者
PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 0f, 1f);
PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 0.5f, 1f);
PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 0.5f, 1f);
// 用一个ObjectAnimator同时动画多个属性
ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(view, alpha, scaleX, scaleY);
animator.setDuration(1000);
animator.start();

效果:View 会在 1 秒内同时从半透明放大到正常大小。

核心方法

java 复制代码
ofFloat(String propertyName, float... values)
ofInt(String propertyName, int... values)
ofObject(String propertyName, TypeEvaluator evaluator, Object... values)
ofKeyframe(String propertyName, Keyframe... values)  // 与关键帧结合使用

PropertyValuesHolder 就是让一个 ObjectAnimator 同时控制多个属性变化的容器。它既能简化代码,又能让动画更流畅、同步。

关键帧(Keyframe)

**Keyframe(关键帧)表示属性在动画过程中的一个"中间状态"。**通过一组关键帧,你可以让属性在动画的不同时间点呈现出不同的值和节奏。

核心方法

java 复制代码
// 关键帧创建
Keyframe.ofFloat(float fraction, float value)    // 在fraction时间点值为value
Keyframe.ofInt(float fraction, int value)
Keyframe.ofObject(float fraction, Object value)

// 设置插值器
setInterpolator(TimeInterpolator interpolator)   // 设置该关键帧段的插值器

示例

java 复制代码
// 创建关键帧:在0%、50%、100%时间点分别设置不同的值和插值器
Keyframe kf0 = Keyframe.ofFloat(0f, 0f);                    // 起点
Keyframe kf1 = Keyframe.ofFloat(0.5f, 300f);               // 中间点
kf1.setInterpolator(new BounceInterpolator());             // 中间段使用弹跳效果
Keyframe kf2 = Keyframe.ofFloat(1f, 200f);                 // 终点

// 将关键帧应用到属性
PropertyValuesHolder pvhY = PropertyValuesHolder.ofKeyframe("translationY", kf0, kf1, kf2);

ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(view, pvhY);
animator.setDuration(2000).start();

三种动画的使用场景和总结

  1. 帧动画:播放一系列图片帧(类似 GIF),性能最差,容易OOM,慎用大图或多帧。适合需要"逐帧播放"的效果,比如加载动画、角色动作、表情动画。

  2. View 动画(补间动画):只改变显示效果,不改变真实位置(坐标不变),性能最好,适合大量元素的简单动画。适合简单的 UI 动效,比如控件出现、隐藏、切换。

  3. 属性动画:直接修改对象属性值,真实改变 View 状态或任意对象的属性。适合复杂的、交互性强的动画,例如按钮点击放大、卡片滑动、颜色渐变等。

动画类型 适合场景 实例
View Animation(补间动画) 简单 UI 动效(出现/隐藏/切换) 按钮淡入、页面切换、列表进入
Drawable Animation(帧动画) 图片帧动画(类似 GIF) 加载动画、角色动作、表情动画
Property Animation(属性动画) 复杂 UI 交互、属性变化 缩放、平移、颜色渐变、旋转、曲线移动

在现在的Android开发中:

  • 属性动画 已经成为主流选择
  • View动画主要用于简单的过渡效果
  • 帧动画 逐渐被LottieSVG动画等替代
  • MotionLayout结合了属性动画和约束布局,成为复杂交互动画的新选择
相关推荐
不良人天码星2 小时前
使用Java连接redis以及开放redis端口的问题
java·开发语言·redis
马克学长2 小时前
SSM村务管理系统s2qnw(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
java·服务器·数据库
羊锦磊2 小时前
[ Spring 框架 ] 数据访问和事务管理
java·后端·spring
未来coding2 小时前
Spring Boot SSE 流式输出,智能体的实时响应
java·spring boot·后端
恸流失3 小时前
java基础-12 : 单列集合(Collection)
java·开发语言·windows
whltaoin3 小时前
Spring Boot自定义全局异常处理:从痛点到优雅实现
java·spring boot·后端
zhangxuyu11183 小时前
Spring boot 学习记录
java·spring boot·学习
元气满满的霄霄3 小时前
Spring Boot整合缓存——Ehcache缓存!超详细!
java·spring boot·后端·缓存·intellij-idea
MClink3 小时前
架构学习之旅-架构的由来
java·学习·架构