虽然在使用上有较大的差异,但是 Transition 框架的实现本质上依赖于属性动画。它的内部核心依赖了 Property Animation(属性动画)机制来完成对视图属性的动态过渡。简而言之,Transition 先去捕捉视图的属性变化,再使用属性动画的方式为这些属性变化创建并执行动画。
1. 核心概念
-
属性动画(Property Animation)
- 引入于 Android 3.0(API 11),也称“View 动画框架的升级”或“动画框架”。
- 通过不断更新对象(通常是 View)的某个或多个“可动画属性”(如
alpha,translationX,rotation,scaleX等),从而改变该对象在屏幕上的外观或位置,形成动画效果。 - 核心类为
ObjectAnimator、ValueAnimator、AnimatorSet等。
-
Transition
- 引入于 Android 4.4(API 19)的 Transition Framework,后来在 AndroidX/Support Library 中也提供支持(
androidx.transition包)。 - 主要用于两个界面状态(Scene)或两个布局状态之间的过渡动画,例如“布局 A” 切换到 “布局 B” 时,系统会自动检测视图的变化(位置、大小、透明度等),并应用相应的过渡动画。
- 核心类为
TransitionManager、TransitionSet、AutoTransition、Fade、Slide、ChangeBounds等。
- 引入于 Android 4.4(API 19)的 Transition Framework,后来在 AndroidX/Support Library 中也提供支持(
2. 主要原理差异
2.1 属性动画(Property Animation)
-
通过 ValueAnimator/ObjectAnimator 修改对象属性值
- 例如
translationX,alpha,rotation等,这些都是真实地改变了 View 在屏幕上的绘制属性。 - 并非只是以前“补间动画”(Tween Animation)的视觉假象,而是真正修改了 View 的坐标、大小、透明度等可绘制属性。
- 例如
-
动画过程可高度自定义
- 可以在
ValueAnimator的updateListener里手动计算并应用任何你想更新的数值(不一定是 View 的属性)。 - 适合灵活地控制复杂动画,如并行动画、序列动画、插值器修改、路径跟随等。
- 可以在
-
重点在“对单个或多个属性的动画控制”
- 属性动画关注的是“我要把哪个值从多少变到多少”,与布局、状态切换没有直接的抽象概念关系。
- 一般做视图层的微动效、拖拽、旋转缩放等动画时,直接用属性动画就可以完成。
2.2 Transition(过渡动画)
-
场景(Scene)与过渡(Transition)
- Transition Framework 提供了“场景”与“过渡”的概念。
- 一个场景(Scene)描述了界面布局的一种静态状态,当从场景 A 切换到场景 B 时,系统通过比较这两个场景中 View 层次、位置、大小的不同,自动生成或执行相应的“过渡动画(Transition)”。
-
自动检测变化、自动应用动画
- 在使用
TransitionManager.beginDelayedTransition(...)或TransitionManager.go(scene, transition)时,你只需要在代码中修改布局约束、View 的可见性等,系统就会自动捕捉到这些变化,并使用指定的 Transition(如ChangeBounds,Fade,Slide等)来在一定的时间内平滑过渡到新的布局状态。
- 在使用
-
重点在“两个状态间的差异”
Transition关注的是不同布局状态的差异,如位置、尺寸、可见性、层级关系等,自动完成从旧状态过渡到新状态。- 常用于界面布局的动态切换,比如在 ConstraintLayout 或者 XML 里把“布局 A”变更为“布局 B”时,想要一个平滑的过渡过程,就可以使用 Transition。
它们虽然都有“动画”二字,但本质针对的使用场景和思路是不同的,一个针对的事View本身,一个针对的事布局状态变化。
属性动画(Property Animation)
-
着眼点:对一个或多个对象(通常是 View)的**“某些属性”**做动画更新,比如
translationX,rotation,alpha等。 -
典型场景:
- 一个按钮点击后要“跳跃”一下;
- 一个列表项被拖拽时跟随手势移动;
- 实现一个复杂的弹簧抖动效果、曲线路径运动等等。
-
重点:你会手动编写“属性从A变到B”的逻辑,自己管理动画的时长、插值方式、更新步骤。如果涉及多个对象或多个属性,还需自己将它们打包成
AnimatorSet做并行或串行执行。
总结一下,属性动画能提供极大的灵活度,想动画化“任何可改变的数值”,基本都能实现。比如,在 ValueAnimator 的监听里,甚至可以更新自己定义的某个“颜色值”或“进度值”,跟 View 并没有直接关系。
Transition(过渡动画)
-
着眼点:两个布局状态(Scene)之间的差异。
-
典型场景:
- 整个界面从“列表模式”切换到“网格模式”,想让每个子项位置和大小平滑过渡;
- ConstraintLayout 里从一种约束切换到另一种约束,比如屏幕旋转导致布局变化、或在 MotionLayout 场景中有一组界面状态切换;
- 打开/收起一个面板时,需要多个视图一起做平滑过渡。
-
重点:不关心每个视图要怎么动;只需告诉系统“我打算把这个布局里的某些东西改成另一种布局(或把某些 View 隐藏)”,系统检测出前后状态变化,然后自动给出一个基于
Fade、Slide、ChangeBounds等预设过渡效果,让界面在一段时间内“平滑”地完成这次变更。
也就是说,Transition 对布局变化非常“友好” ——改布局就行,系统自动捕捉位置、大小、可见性变化并执行过渡。不需要一个个写“这个 View 的 X 坐标从 100 变到 200”之类的逻辑。
3. 常见使用场景对比
| 使用场景 | 属性动画(Property Animation) | Transition(过渡动画) |
|---|---|---|
| 单个元素的微动效 | 常用 直接通过 ObjectAnimator 对某个视图属性做动画,比如按钮点击缩放、View滑动 | 不常用 因为 Transition 更适合多个视图、多个布局的整体切换 |
| 多元素复杂动画 | 常用 可以使用 AnimatorSet 将多个 ObjectAnimator 串行或并行执行 | 也可用 如果是多 View 的共同过渡,需要同时变更布局中的多个元素位置或可见性,Transition 也能一次性搞定 |
| 布局之间的切换 | 可行 但需手动写逻辑,监听动画进度去更新 View 的位置、大小等 | 常用 只需要改变布局或约束,然后调用 TransitionManager.beginDelayedTransition(...),系统自动检测并执行动画 |
| 响应布局变化 | 如果布局大小或位置变化,需要手动更新或重新执行动画 | 可以自动检测 View 的位置、大小、可见性的改变,并做过渡动画 |
| 代码复杂度 | 动画逻辑都需要手动编写、维护,灵活但繁琐 | 定义或选择合适的 Transition(如 AutoTransition, ChangeBounds),然后只需写布局变更逻辑即可 |
4. 优点与不足
4.1 属性动画(Property Animation)
优点:
- 非常灵活,能动画任何可控的属性(不仅仅是 View,也可以是自定义对象的任意字段)。
- 结合插值器(Interpolator)和估值器(TypeEvaluator)可以做出各式各样复杂的效果。
- 可与手势交互、拖拽或绘图等需求深度结合。
不足:
- 一旦场景涉及多个视图或布局的大幅变动,需要繁琐地控制每个视图的动画。
- 无法自动侦测布局改变,需要自己手动计算、设置动画目标值。
4.2 Transition(过渡动画)
优点:
- 针对布局或场景切换时的过渡动画,使用非常高效、方便。只需写布局变化,无需关心具体动画细节。
- 系统自动处理视图的“初始状态”与“目标状态”,减少大量繁琐的动画计算代码。
- 有现成的
Fade,Slide,ChangeBounds等常用过渡类型,也可以组合或自定义。
不足:
- 不适合“单个元素的精细动画”或“自定义属性动画”。Transition 更多是布局层面、场景层面的大范围切换。
- 相比属性动画,自定义空间相对较小(虽然也能写
Transition子类,但门槛更高)。 - 如果只是做些旋转、弹跳、局部动效,使用 Transition 可能有些“大材小用”且不够灵活。
5. 具体使用示例对比
5.1 使用属性动画实现一个简单移动
// 属性动画移动示例
val view: View = findViewById(R.id.myView)
val animator = ObjectAnimator.ofFloat(view, "translationX", 0f, 300f)
animator.duration = 500
animator.start()
- 直接改变
translationX属性,使view在 0 ~ 300f 间移动。
5.2 使用 Transition 进行布局切换
// layoutA.xml -> layoutB.xml 过渡示例
TransitionManager.beginDelayedTransition(constraintLayout, AutoTransition())
// 例如,更改某个按钮的可见性
button.visibility = if (button.visibility == View.VISIBLE) View.GONE else View.VISIBLE
// 更改某个View的约束属性等
val params = myView.layoutParams as ConstraintLayout.LayoutParams
params.topMargin = 200
myView.layoutParams = params
- 当调用
TransitionManager.beginDelayedTransition(...)后,再修改布局属性,系统会自动对位置、大小、可见性变化做动画过渡。
6. 如何选择?
- 如果只是做一个(或几个)视图的微动效(如透明度、移动、旋转、缩放),并不涉及布局切换或多视图的同步动画,优先使用“属性动画(Property Animation)”。
- 如果要对整个布局或多个视图进行统一过渡(比如点击一个按钮后页面某几个区域同时从显示变隐藏,或从一个ConstraintSet切换到另一个ConstraintSet)而且想要自动过渡,则使用“Transition Framework”来简化操作。
- 如果需要同时兼顾布局切换和一些复杂的自定义动画,就需要把 Property Animation 和 Transition 结合来用,或者在 Transition 中自定义
Transition子类来处理更复杂的属性变换。
7. 为何说Transition 的实现本质上是属性动画?
1. Transition 本身不直接绘制动画
-
Transition主要做的事是:- 捕捉某些感兴趣的视图属性(如位置、大小、透明度等)的 开始值 和 结束值。
- 对比这些属性的差异,决定要对哪些属性执行动画及如何执行。
- 创建并管理这些动画对象的生命周期(开始、结束、取消等)。
-
但
Transition并不直接完成对属性的“插值计算”或“实时绘制”。它最终会将属性的变更交给动画系统去插值、去更新,这个动画系统就是 属性动画(Property Animation)。
2. Transition 内部通过创建 Animator(通常是 ObjectAnimator)实现动画
- 无论是系统自带的
Fade/Slide/ChangeBounds等过渡动画,还是自定义Transition,它们在内部真正做动画时,都要通过Animator来改变视图属性。 Animator的具体实现常见于ObjectAnimator.ofFloat()、ObjectAnimator.ofInt()等方法,即标准的 属性动画 API。
示例简析(以 Fade 为例):
java
复制代码
@Override
public Animator createAnimator(
ViewGroup sceneRoot,
TransitionValues startValues,
TransitionValues endValues
) {
if (startValues == null || endValues == null) {
return null;
}
View view = endValues.view;
float startAlpha = (float) startValues.values.get(PROPNAME_ALPHA);
float endAlpha = (float) endValues.values.get(PROPNAME_ALPHA);
// 如果 alpha 有变化,就创建一个 ObjectAnimator
if (startAlpha != endAlpha) {
view.setAlpha(startAlpha);
return ObjectAnimator.ofFloat(view, View.ALPHA, startAlpha, endAlpha);
}
return null;
}
如上所示,过渡动画的核心正是 ObjectAnimator.ofFloat(...),它背后的执行逻辑就是“属性动画”在插值并更新 alpha 值,才实现了淡入淡出的过渡。
3. “捕捉→对比→动画” 这一套流程天然契合属性动画
Transition 的大体思路是:
- 捕捉初始属性值(
captureStartValues) - 捕捉结束属性值(
captureEndValues) - 对比初始值和结束值,生成对应的 Animator(
createAnimator)
而属性动画的使用方式正是:
- 设置动画的起始值和结束值
- 在动画的每一帧,通过插值器插值,计算当前帧的属性值
- 将属性值应用到对象上
两者逻辑上高度重合。
4. 自定义 Transition 依旧是通过自定义属性动画实现
如果需要自定义动画,比如让视图做旋转或自定义路径运动,你需要在自定义的 Transition 中:
- 在
captureStartValues和captureEndValues中记录自定义的属性(如rotation)。 - 在
createAnimator中根据属性变化创建Animator,通常是ObjectAnimator.ofFloat(view, "rotation", startRotation, endRotation)。
这依旧使用了属性动画的 API 来进行插值和更新属性,也再次说明了 Transition 只是一个管理过渡动画的框架,而 真正驱动属性变化的仍是属性动画。
5. Transition 框架只是对“视图变化场景”进行封装
- 从使用角度看,
TransitionManager.beginDelayedTransition()会把视图层级的变化(添加、移除、Layout参数改变等)捕捉下来,并自动触发对应过渡动画。 - 但一旦需要执行动画,它还是要去生成
Animator,而Animator的实质就是“属性动画”。
因此,Transition 并不是一个替换或摆脱“属性动画”概念的全新东西,而是基于属性动画之上的更高层封装,帮助开发者在布局发生变化时,更简洁、更自动化地完成动画过渡。
总结
“Transition 的实现本质上是属性动画” ,指的就是:
Transition并不自行插值绘制,而是通过捕捉前后属性 → 生成Animator→ 交给系统属性动画运行。Transition内部大量调用ObjectAnimator或ValueAnimator这些属性动画 API,用于具体完成视图属性的过渡。- 从原理到执行流程,都离不开属性动画的插值机制和属性更新机制。
所以,Transition 说到底是“在布局变化前后,利用属性动画去驱动视图属性变化”
8. 总结
- 属性动画 专注于 某些属性值 随时间变化,可以对任何对象的任意字段应用动画,灵活但需要手动编写细节,适合微动效或需要高度控制的场景。
- Transition 专注于 界面布局状态 之间的切换,自动侦测变化,省去了手动计算,非常适合界面整体的过渡、多视图同步的布局变化场景。
-
两套动画机制服务于两种不同层级的需求:
- Property Animation:更底层、更灵活,可应用于任何可更新数值的对象属性。强调的是“我想让这个值随时间变化到另一个值”。
- Transition:更高层、更关注“从一个界面/布局状态过渡到另一个状态”,系统自动侦测哪里变了、需要怎么动。
-
它们各有用武之地:
- 如果只是移动/淡出/旋转某个单个 View,写属性动画更直观。
- 如果要做大范围布局切换,让一堆 View 同步变化位置和可见性,用 Transition(或 MotionLayout)更省心。
-
动画效果类似≠开发思路相同:
- 最终看起来都是“会动的界面”,但实际实现原理、编写方式、可扩展性都不一样。
- 因此 Android 提供了两套动画方案,满足不同层次的需求。
所以,并不是说“为什么要搞两套,合并成一个不行吗?” ——它们虽然都有“动画”二字,但本质针对的使用场景和思路是不同的。正因为场景不同,所以两套机制各自精简、各自强大,合在一起反倒会变得臃肿、难以维护。选择合适的工具来处理相对应的场景,才是最有效率的做法。