【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"
:动画开始时的角度,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
)。子动画:
- alpha :从完全透明
0.0
→ 完全不透明1.0
(淡入)。- scale :从
0.5
倍大小 →1.0
正常大小(中心点在控件的50%
,即正中间)。效果 :控件在 600ms 内,从小且透明 → 放大并逐渐显示,就像"弹出淡入"的效果。

常用属性详解
-
duration
:动画持续时间(毫秒) -
startOffset
:动画开始前的延迟时间(毫秒) -
repeatCount
:重复次数(整数或" infinite ") -
repeatMode
:restart
:从头开始重复(默认)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>
- 第一个
<scale>
fromXScale="1.0"
→toXScale="0.9"
fromYScale="1.0"
→toYScale="0.9"
- 在 100 毫秒 内,控件从正常大小缩小到 90%。
- 看起来就像"按下去"的效果。
- 第二个
<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(每个子动画如果没单独设置,会用这个时间)。子动画:
- translate
fromYDelta="20%"
→toYDelta="0"
- 控件从自身高度的 20% 下方 位置,向上移动到原位。
@android:anim/decelerate_interpolator
:减速插值器,开始快 → 结束慢。- 效果:控件 向上滑入。
- 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 被打开时,所需的动画资源 idexitAnim
: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) // 数值更新监听
使用流程:
- 创建
ValueAnimation
,设置目标属性的起始值、结束值和持续时间。 - 添加更新监听器,在监听器中获取当前计算出的值。
- 手动将这个值赋值给目标对象的属性。
示例:让一个 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
正常工作,目标对象必须满足以下至少一个条件:
- 拥有该属性的
setter
方法:例如属性叫foo
,则需要有setFoo()
方法。 - 使用包装器:如果对象没有
setter
,你可以提供一个包装器,并通过它来设置值。 - 或者,你可以直接改变该属性的值(不推荐,违背封装原则)。
示例:将一个 View 在水平方向上平移 500px。
java
// 参数:目标对象,属性名(字符串),起始值,结束值
ObjectAnimator anim = ObjectAnimator.ofFloat(myView, "translationX", 0f, 300f);
anim.setDuration(1000);
anim.start();
这段代码背后发生了什么?
ObjectAnimator
会计算从 0f 到 500f 之间的一系列浮点数。- 对于每一个计算出的值
value
,它会去调用myView.setTranslationX(value)
方法。- 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
,float
或color
这些基本类型时,或者你想对基本类型的变换规则进行自定义时,就需要它。它根据 当前动画进度的百分比(插值器计算出的) 和 起始值、结束值 ,计算出当前帧的具体属性值。 - 简单来说:插值器输出"进度",估值器根据这个"进度"算出"当前值"。
- 系统内置的估值器 :
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 属性时的首选。
属性动画的监听器
通过 AnimatorListener
和 AnimatorUpdateListener
来监听动画的各种状态。
-
AnimatorListener
:监听开始、结束、取消、重复。javaanimator.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
,只重写你需要的方法。javaanimator.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();
三种动画的使用场景和总结
-
帧动画:播放一系列图片帧(类似 GIF),性能最差,容易OOM,慎用大图或多帧。适合需要"逐帧播放"的效果,比如加载动画、角色动作、表情动画。
-
View 动画(补间动画):只改变显示效果,不改变真实位置(坐标不变),性能最好,适合大量元素的简单动画。适合简单的 UI 动效,比如控件出现、隐藏、切换。
-
属性动画:直接修改对象属性值,真实改变 View 状态或任意对象的属性。适合复杂的、交互性强的动画,例如按钮点击放大、卡片滑动、颜色渐变等。
动画类型 | 适合场景 | 实例 |
---|---|---|
View Animation(补间动画) | 简单 UI 动效(出现/隐藏/切换) | 按钮淡入、页面切换、列表进入 |
Drawable Animation(帧动画) | 图片帧动画(类似 GIF) | 加载动画、角色动作、表情动画 |
Property Animation(属性动画) | 复杂 UI 交互、属性变化 | 缩放、平移、颜色渐变、旋转、曲线移动 |
在现在的Android开发中:
- 属性动画 已经成为主流选择
- View动画主要用于简单的过渡效果
- 帧动画 逐渐被Lottie 、SVG动画等替代
- MotionLayout结合了属性动画和约束布局,成为复杂交互动画的新选择