视图动画(View Animation)
视图动画是 Android 早期的动画框架,主要作用于View
,通过改变视觉效果实现动画,但不改变 View 的实际属性(如位置、大小)。
1. 补间动画(Tween Animation)
通过定义动画开始和结束的关键帧,系统自动计算中间帧,实现平移、缩放、旋转、透明度等效果。
-
核心类型(4 种基础动画 + 组合动画):
- 平移(translate) :改变 View 的视觉位置,参数包括
fromXDelta
(起始 X)、toXDelta
(结束 X)、fromY Delta
(起始 Y)、toYDelta
(结束 Y)(单位:px 或 % p,% p 表示相对于父容器的百分比)。 - 缩放(scale) :改变 View 的视觉大小,参数包括
fromXScale
(起始 X 缩放比)、toXScale
(结束 X 缩放比)、fromYScale
、toYScale
,以及pivotX
、pivotY
(缩放中心点,默认 View 左上角)。 - 旋转(rotate) :围绕中心点旋转,参数包括
fromDegrees
、toDegrees
,pivotX
、pivotY
(旋转中心)。 - 透明度(alpha) :改变 View 的透明度,参数
fromAlpha
(起始透明度,0-1)、toAlpha
(结束透明度)。 - 组合动画(set) :通过
<set>
标签组合多个动画,可设置ordering
(播放顺序:together
同时 /sequentially
顺序)。
- 平移(translate) :改变 View 的视觉位置,参数包括
-
实现方式:
- XML 定义 :在
res/anim/
目录下创建动画文件(如translate_anim.xml
),通过AnimationUtils.loadAnimation(context, R.anim.xxx)
加载,再调用view.startAnimation(animation)
启动。 - 代码定义 :直接创建
TranslateAnimation
、ScaleAnimation
等对象,设置参数后启动。
- XML 定义 :在
-
局限性:
- 仅改变 View 的视觉效果,不修改 View 的实际属性(如
left
、top
),因此点击事件仍在原位置响应。 - 仅支持
View
对象,无法对非 View 对象执行动画
- 仅改变 View 的视觉效果,不修改 View 的实际属性(如
平移
xml
<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromXDelta="0%p"
android:toXDelta="50%p"
android:fromYDelta="0%p"
android:toYDelta="0%p"
android:duration="1000"
android:fillAfter="true"/>
java
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main);
ImageView view = findViewById(R.id.iv_target);
// 加载XML动画
Animation translateAnim = AnimationUtils.loadAnimation(this, R.anim.translate);
view.startAnimation(translateAnim);
// 纯Java实现
TranslateAnimation translate = new TranslateAnimation(
0, 500, // X轴起始值和结束值(像素)
0, 0 // Y轴起始值和结束值
);
translate.setDuration(1000);
translate.setFillAfter(true);
view.startAnimation(translate);
}
}
缩放
这个动画会使指定的 View 从原始大小均匀地放大到原来的 2 倍大小,缩放的中心点在 View 的中心位置,整个动画过程持续 1 秒。
xml
<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:fromXScale="1.0"
android:toXScale="2.0"
android:fromYScale="1.0"
android:toYScale="2.0"
android:pivotX="50%"
android:pivotY="50%"
android:duration="1000"/>
java
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main);
ImageView view = findViewById(R.id.iv_target);
// 加载XML动画
Animation scaleAnim = AnimationUtils.loadAnimation(this, R.anim.scale);
view.startAnimation(scaleAnim);
// 纯Java实现
ScaleAnimation scale = new ScaleAnimation(
1.0f, 2.0f, // X轴缩放起始值和结束值
1.0f, 2.0f, // Y轴缩放起始值和结束值
Animation.RELATIVE_TO_SELF, 0.5f, // 缩放中心点X
Animation.RELATIVE_TO_SELF, 0.5f // 缩放中心点Y
);
scale.setDuration(1000);
view.startAnimation(scale);
}
}
旋转
这个动画会使指定的 View 绕自身的中心点顺时针旋转一周(360 度),动画过程持续 2 秒,并且整个动画会重复播放 10 次。
xml
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromDegrees="0"
android:toDegrees="360"
android:pivotX="50%"
android:pivotY="50%"
android:duration="2000"
android:repeatCount="10"/>
java
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main);
ImageView view = findViewById(R.id.iv_target);
// 加载XML动画
Animation rotateAnim = AnimationUtils.loadAnimation(this, R.anim.rotate);
view.startAnimation(rotateAnim);
// 纯Java实现
RotateAnimation rotate = new RotateAnimation(
0, 360, // 起始角度和结束角度
Animation.RELATIVE_TO_SELF, 0.5f, // 旋转中心点X
Animation.RELATIVE_TO_SELF, 0.5f // 旋转中心点Y
);
rotate.setDuration(2000);
rotate.setRepeatCount(10); // 重复10次
view.startAnimation(rotate);
}
}
透明度
这个动画是指定的 View 在 1 秒内从完全不透明逐渐变为部分透明(透明度为 0.3)。
xml
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:fromAlpha="1.0"
android:toAlpha="0.3"
android:duration="1000"/>
java
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main);
ImageView view = findViewById(R.id.iv_target);
// 加载XML动画
Animation alphaAnim = AnimationUtils.loadAnimation(this, R.anim.alpha);
view.startAnimation(alphaAnim);
// 纯Java实现
AlphaAnimation alpha = new AlphaAnimation(1.0f, 0.3f);
alpha.setDuration(1000);
view.startAnimation(alpha);
}
}
组合动画
整个组合动画的执行过程是:平移动画先开始执行,1 秒后缩放动画开始执行,再过 0.5 秒后旋转动画开始执行,每个动画的持续时间都是 1 秒。
xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="true"
android:repeatCount="5"
android:repeatMode="restart">
<translate
android:fromXDelta="0%p"
android:toXDelta="50%p"
android:duration="1000"/>
<scale
android:fromXScale="1.0"
android:toXScale="1.5"
android:fromYScale="1.0"
android:toYScale="1.5"
android:pivotX="50%"
android:pivotY="50%"
android:startOffset="500"
android:duration="1000"/>
<rotate
android:fromDegrees="0"
android:toDegrees="90"
android:pivotX="50%"
android:pivotY="50%"
android:startOffset="1000"
android:duration="1000"/>
</set>
java
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main);
ImageView view = findViewById(R.id.iv_target);
// 加载XML动画
Animation setAnim = AnimationUtils.loadAnimation(this, R.anim.combined);
view.startAnimation(setAnim);
// 纯Java实现
AnimationSet set = new AnimationSet(true); // true表示共享插值器
TranslateAnimation translate = new TranslateAnimation(0, 500, 0, 0);
translate.setDuration(1000);
ScaleAnimation scale = new ScaleAnimation(
1.0f, 1.5f, 1.0f, 1.5f,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f
);
scale.setStartOffset(500);
scale.setDuration(1000);
RotateAnimation rotate = new RotateAnimation(
0, 90,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f
);
rotate.setStartOffset(1000);
rotate.setDuration(1000);
set.setRepeatMode(Animation.RESTART);
set.setRepeatCount(5);
set.addAnimation(translate);
set.addAnimation(scale);
set.addAnimation(rotate);
view.startAnimation(set);
}
}
2. 帧动画(Frame Animation)
通过逐帧播放一系列图片实现动画(类似胶片动画),本质是Drawable
的一种(AnimationDrawable
)。
-
实现方式:
- XML 定义 :在
res/drawable/
目录下创建animation-list
资源(如frame_anim.xml
),每个<item>
指定图片和显示时长,通过view.setBackgroundResource(R.drawable.xxx)
设置,再强转为AnimationDrawable
启动(start()
)。 - 代码定义 :直接创建
AnimationDrawable
,调用addFrame(drawable, duration)
添加帧,再启动。
- XML 定义 :在
-
注意事项:
- 图片资源过大易导致
OOM
(内存溢出),需控制图片数量和分辨率。 - 必须在
onWindowFocusChanged
或post(Runnable)
中调用start()
(避免视图未初始化完成),且在Activity
销毁前调用stop()
,防止内存泄漏。
- 图片资源过大易导致
xml
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false"> <!-- oneshot 属性:true 表示动画只播放一次,false 表示循环播放 -->
<!-- 帧 1 -->
<item
android:drawable="@drawable/frame1"
android:duration="200" /> <!-- 每帧显示的持续时间(毫秒) -->
<!-- 帧 2 -->
<item
android:drawable="@drawable/frame2"
android:duration="200" />
<!-- 帧 3 -->
<item
android:drawable="@drawable/frame3"
android:duration="200" />
<!-- 帧 4 -->
<item
android:drawable="@drawable/frame4"
android:duration="200" />
</animation-list>
xml
<ImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true" />
java
ImageView imageView = findViewById(R.id.imageView);
// 加载帧动画资源
AnimationDrawable frameAnimation = (AnimationDrawable) getResources().getDrawable(R.drawable.frame_animation);
imageView.setImageDrawable(frameAnimation);
// 启动帧动画
frameAnimation.start();
属性动画
API 11(Android 3.0)引入,解决视图动画的局限性,直接修改对象的属性值(包括 View 和非 View 对象),支持更灵活的动画效果。
1. 核心原理
通过不断修改对象的属性值(如x
、y
、alpha
),并在属性变化时触发视图重绘,实现动画效果。核心是 "值的变化"+"属性的更新"。
2. 核心类
-
ValueAnimator :属性动画的核心,负责计算动画过程中的中间值,通过
addUpdateListener
监听值变化,手动更新目标属性。javaProgressBar progressBar = findViewById(R.id.progressBar); ValueAnimator valueAnimator = ValueAnimator.ofInt(0,100); valueAnimator.setDuration(3000); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(@NonNull ValueAnimator animation) { Integer value = (Integer) animation.getAnimatedValue(); progressBar.setProgress(value); } }); valueAnimator.start();
-
ObjectAnimator :
ValueAnimator
的子类,可直接操作对象的属性(需属性有setter
方法,若从非默认值开始,还需getter
)。javaButton button = findViewById(R.id.btn); // 创建一个 ObjectAnimator,让按钮在 X 轴上从当前位置移动到 200px 的位置 ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(button, "translationX", 0f, 200f); objectAnimator.setDuration(1000); // 动画持续时间 1000 毫秒 objectAnimator.start(); // 启动动画
-
AnimatorSet :组合多个
Animator
(ValueAnimator
/ObjectAnimator
),支持顺序、并行或延迟执行。javaImageView imageView = findViewById(R.id.imageView); ObjectAnimator translateAnimator = ObjectAnimator.ofFloat(imageView, "translationX", 0f, 200f) .setDuration(1000); ObjectAnimator rotateAnimator = ObjectAnimator.ofFloat(imageView,"Rotation", 0f, 360f) .setDuration(1000); AnimatorSet animatorSet = new AnimatorSet(); // 动画按顺序 animatorSet.playSequentially( translateAnimator, rotateAnimator ); // 动画同时 animatorSet.playTogether( translateAnimator, rotateAnimator ); animatorSet.start();
3. 关键概念
-
插值器(Interpolator) :控制动画的速率曲线(如加速、减速),常用系统插值器:
LinearInterpolator
:匀速。AccelerateInterpolator
:加速。DecelerateInterpolator
:减速。AccelerateDecelerateInterpolator
:先加速后减速。BounceInterpolator
:弹跳效果(类似小球落地)。OvershootInterpolator
:超过目标值后回弹。
-
估值器(TypeEvaluator) :计算属性值的具体变化方式,系统提供:
IntEvaluator
:整数类型。FloatEvaluator
:浮点类型。ArgbEvaluator
:颜色值(int
类型的 ARGB)。- 自定义估值器:实现
TypeEvaluator<T>
接口,如实现坐标点(PointF
)的贝塞尔曲线动画。
-
常用属性:
duration
:动画时长(毫秒)。repeatCount
:重复次数(ValueAnimator.INFINITE
表示无限重复)。repeatMode
:重复模式(RESTART
从头开始 /REVERSE
反向播放)。startDelay
:延迟启动时间(毫秒)。
4. 实现方式
- 代码实现:推荐,更灵活(如动态计算属性值)。
- XML 实现 :在
res/animator/
目录下定义(如object_anim.xml
),通过AnimatorInflater.loadAnimator(context, R.animator.xxx)
加载。
5. 优势与注意事项
-
优势:
- 可操作任意对象的任意属性(不仅限于 View)。
- 真正修改属性值,解决视图动画的点击事件问题。
- 支持更复杂的动画(如路径动画、颜色渐变)。
-
注意事项:
-
操作自定义属性时,需确保属性有
setter
(和getter
)方法,或通过包装类间接操作。 -
避免在动画中频繁触发
layout
、measure
、draw
(如修改width
、height
),优先使用translationX/Y
、scaleX/Y
、alpha
等属性(硬件加速优化)。 -
动画结束后及时取消(
cancel()
),避免内存泄漏(尤其在Activity
销毁时)。
-
转场动画(Transition Animation)
API 21(Android 5.0)引入,用于页面(Activity/Fragment)切换或视图集合(ViewGroup)的布局变化时的动画,增强页面跳转的连贯性。
1. 核心概念
-
场景(Scene) :表示布局的一种状态(如初始状态、目标状态),通过
Scene.getSceneForLayout(viewGroup, layoutId, context)
创建。 -
转场(Transition) :定义场景切换时的动画规则,系统提供常用转场:
Explode
:爆炸效果(视图从中心向外扩散或向内聚集)。Slide
:滑动效果(从边缘滑入 / 滑出)。Fade
:淡入淡出效果。ChangeBounds
:布局变化时,View 的位置和大小变化动画。ChangeTransform
:View 的旋转、缩放变化动画。ChangeImageTransform
:图片的缩放、裁剪变化动画。
2. 应用场景
-
Activity 转场:
- 普通转场:通过
getWindow().setEnterTransition(transition)
设置进入 / 退出转场。 - 共享元素转场:两个 Activity 中相同的元素(如图片)平滑过渡,需在布局中设置
android:transitionName
,启动时通过ActivityOptions.makeSceneTransitionAnimation(activity, sharedElement, transitionName)
传递。
- 普通转场:通过
-
Fragment 转场 :类似 Activity,通过
setEnterTransition()
、setSharedElementEnterTransition()
等方法设置。 -
布局变化(Layout Transition) :通过
TransitionManager.beginDelayedTransition(viewGroup, transition)
,在 ViewGroup 的布局变化(如添加 / 移除子 View)时自动应用动画。
矢量动画(AnimatedVectorDrawable, AVD)
用于对矢量图(VectorDrawable
)的路径(path
)或属性(如fillColor
)执行动画,优点是缩放不失真、文件体积小。
1. 组成部分
- VectorDrawable :定义矢量图(
res/drawable/vector.xml
),通过<path>
标签描述图形轮廓。 - AnimatedVectorDrawable :关联矢量图和动画(
res/drawable/animated_vector.xml
),指定需要动画的path
或group
,以及对应的动画资源。 - 动画定义 :通过
ObjectAnimator
或ValueAnimator
定义具体动画(如路径变形、颜色渐变)。
2. 适用场景
简单图标动画(如加载指示器、开关状态切换、菜单展开)。
特殊场景动画
- RecyclerView Item 动画 :通过
ItemAnimator
控制 Item 的添加、移除、移动动画,默认使用DefaultItemAnimator
,可自定义(继承SimpleItemAnimator
)。 - 列表滚动动画 :Item 随滚动渐入(如
alpha
从 0 到 1,translationY
从 50 到 0),通过RecyclerView.OnScrollListener
监听滚动状态,结合ViewPropertyAnimator
实现。 - 下拉刷新动画 :
SwipeRefreshLayout
自带刷新动画,可通过setProgressDrawable()
自定义。 - 加载动画 :自定义
View
绘制循环动画(如圆形进度条、脉冲效果),使用ValueAnimator
驱动绘制参数变化。
动画优化与性能
- 减少过度绘制 :避免动画视图叠加过多,使用
android:clipChildren="false"
减少裁剪开销。 - 控制动画数量:避免同时运行大量动画,优先保证关键动画流畅。
- 硬件加速 :开启硬件加速(
android:hardwareAccelerated="true"
)提升渲染性能,但注意部分绘制操作(如Canvas.clipPath
)在硬件加速下可能异常。 - 避免耗时操作 :动画回调(如
onAnimationUpdate
)中不执行耗时任务(如 IO、复杂计算),避免掉帧。 - 内存管理 :帧动画及时停止并释放资源,
Activity
销毁时取消所有动画(animator.cancel()
)。
兼容性与扩展
-
兼容性 :API 11 以下使用视图动画,API 11 + 使用属性动画;通过
ViewPropertyAnimator
(API 12+)简化属性动画调用(view.animate().translationX(100).start()
)。 -
第三方库:
- Lottie:Airbnb 开源库,支持 After Effects 导出的动画(JSON 格式),无需手动编写代码,适合复杂动画(如节日主题、广告动画)。
- Glide:加载 GIF 或视频作为动画,适合简单循环动画。