动画与过渡
使用 ViewPropertyAnimator、ObjectAnimator 和 AnimatorSet 为视图添加动画。添加 Fragment 过渡、共享元素动画和弹簧物理效果。
动画与过渡 是 CoddyKit 上的免费 Android Academy 课时。 这是第 4 节课,共 5 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Android Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Android Academy 课程共包含 5 节课。
动画为何重要
动画让应用显得鲜活且响应迅速。它们可以:
- 为用户操作提供视觉反馈
- 展示屏幕之间的关联
- 让数据变化不那么突兀
- 帮助用户理解发生了什么变化以及变化原因
关键原则是:动画应服务于界面,而不是分散用户注意力。时长应控制在 300 毫秒以内。
ViewPropertyAnimator — 快速动画
为视图添加动画的最简单方式。链式调用后调用 start():
// Fade a view out:
binding.tvMessage
.animate()
.alpha(0f)
.setDuration(300)
.withEndAction { binding.tvMessage.visibility = View.GONE }
.start()
// Slide in from bottom:
binding.cardView.translationY = 200f
binding.cardView.alpha = 0f
binding.cardView
.animate()
.translationY(0f)
.alpha(1f)
.setDuration(250)
.setInterpolator(DecelerateInterpolator())
.start()ObjectAnimator
ObjectAnimator 可以按名称为任意对象的任意属性添加动画。它比 ViewPropertyAnimator 更灵活:
// Rotate an image:
val rotateAnimator = ObjectAnimator.ofFloat(
binding.ivLogo,
"rotation",
0f, 360f
).apply {
duration = 600
repeatCount = ObjectAnimator.INFINITE
interpolator = LinearInterpolator()
start()
}
// Stop it:
rotateAnimator.cancel()AnimatorSet — 顺序与并行
AnimatorSet 用于协调多个动画器同时运行或依次运行:
val fadeIn = ObjectAnimator.ofFloat(binding.card, "alpha", 0f, 1f).setDuration(300)
val scaleX = ObjectAnimator.ofFloat(binding.card, "scaleX", 0.8f, 1f).setDuration(300)
val scaleY = ObjectAnimator.ofFloat(binding.card, "scaleY", 0.8f, 1f).setDuration(300)
val slideUp = ObjectAnimator.ofFloat(binding.card, "translationY", 100f, 0f).setDuration(300)
AnimatorSet().apply {
// Run fadeIn + scaleX + scaleY together, then slideUp
play(fadeIn).with(scaleX).with(scaleY)
play(slideUp).after(fadeIn)
interpolator = DecelerateInterpolator()
start()
}插值器
插值器控制动画变化的速率。常见的插值器包括:
LinearInterpolator— 匀速AccelerateInterpolator— 开始较慢,逐渐加速(退出效果)DecelerateInterpolator— 开始较快,逐渐减速(进入效果)AccelerateDecelerateInterpolator— 慢 → 快 → 慢(默认)OvershootInterpolator— 超过目标后弹回BounceInterpolator— 在结束时弹跳
TransitionManager — 布局变化
在更改可见性或布局属性之前调用 TransitionManager.beginDelayedTransition(),即可自动为布局变化添加动画:
binding.btnToggle.setOnClickListener {
// Tell the framework to animate the next layout change
TransitionManager.beginDelayedTransition(
binding.root,
ChangeBounds().apply { duration = 300 }
)
// Now change visibility/size — Android animates the difference automatically
binding.expandedSection.visibility =
if (binding.expandedSection.isVisible) View.GONE else View.VISIBLE
}Fragment 过渡
为 Fragment 事务添加进入和退出动画:
supportFragmentManager.beginTransaction()
.setCustomAnimations(
R.anim.slide_in_right, // enterAnim
R.anim.slide_out_left, // exitAnim
R.anim.slide_in_left, // popEnterAnim
R.anim.slide_out_right // popExitAnim
)
.replace(R.id.container, DetailFragment())
.addToBackStack(null)
.commit()
// Or use MaterialSharedAxis for a modern, Material Motion feel:
val forward = MaterialSharedAxis(MaterialSharedAxis.X, true)
exitTransition = forward
enterTransition = MaterialSharedAxis(MaterialSharedAxis.X, true)共享元素过渡
在两个屏幕之间共享一个视图,使其从一个位置动画移动到另一个位置(例如,从缩略图展开为完整图片):
// In ListFragment:
binding.ivThumbnail.transitionName = "hero_image_${item.id}"
val extras = FragmentNavigatorExtras(
binding.ivThumbnail to "hero_image_${item.id}"
)
findNavController().navigate(R.id.action_list_to_detail, null, null, extras)
// In DetailFragment:
sharedElementEnterTransition = TransitionInflater
.from(requireContext())
.inflateTransition(android.R.transition.move)
// ImageView in detail layout must have the same transitionName弹簧动画
弹簧动画通过模拟真实的弹簧物理效果,比线性动画或减速动画更自然:
implementation 'androidx.dynamicanimation:dynamicanimation:1.0.0'
// Spring animation on a button press:
val spring = SpringAnimation(binding.fabAdd, DynamicAnimation.SCALE_X, 1f).apply {
spring = SpringForce(1f).apply {
stiffness = SpringForce.STIFFNESS_MEDIUM
dampingRatio = SpringForce.DAMPING_RATIO_LOW_BOUNCY
}
}
binding.fabAdd.setOnClickListener {
binding.fabAdd.scaleX = 0.8f
spring.start()
}Lottie — 复杂动画
Lottie 可以播放导出为 JSON 的 After Effects 动画。非常适合加载指示器、引导页和空状态:
// app/build.gradle:
// implementation 'com.airbnb.android:lottie:6.4.0'
<!-- In layout XML -->
<com.airbnb.lottie.LottieAnimationView
android:id="@+id/lottieView"
android:layout_width="200dp"
android:layout_height="200dp"
app:lottie_rawRes="@raw/loading_animation"
app:lottie_autoPlay="true"
app:lottie_loop="true" />
// Control in code:
binding.lottieView.playAnimation()
binding.lottieView.pauseAnimation()动画最佳实践
实现流畅且无障碍动画的指南:
- 大多数界面动画的时长设为 150–300 毫秒
- 始终遵循
AnimatorUtils.areAnimatorsEnabled()或Settings.System.ANIMATOR_DURATION_SCALE - 前庭功能障碍用户可能会停用动画 — 检查无障碍设置中的
Reduce Motion - 不要为不需要动画的内容添加动画 — 少即是多
- 在低端设备上测试(动画可能导致掉帧)
快速检查
如果希望元素进入屏幕后呈现减速并稳定下来的效果,哪个插值器最合适?
回顾:动画与过渡
通过流畅的动画让您的应用栩栩如生:
view.animate()— 快速实现透明度、平移、缩放和旋转ObjectAnimator— 支持任意属性,并提供精细控制AnimatorSet— 编排多个动画器(同时执行/依次执行)TransitionManager.beginDelayedTransition()— 自动为布局变化添加动画- 共享元素过渡 — 在不同屏幕之间实现无缝衔接
- Lottie — 播放来自 After Effects 的复杂 JSON 动画
下一节:适用于多区域应用的底部导航和标签页。
常见问题解答
「动画与过渡」课时是免费的吗?
是的 — 「动画与过渡」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Android Academy 课程的其余内容,请升级到 CoddyKit PRO。 Android Academy 课程共包含 5 节课。
「动画与过渡」这节课中我会学到什么?
使用 ViewPropertyAnimator、ObjectAnimator 和 AnimatorSet 为视图添加动画。添加 Fragment 过渡、共享元素动画和弹簧物理效果。 你通过在浏览器中直接运行的动手代码来练习 Android Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Android Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Android Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 5 节。
「动画与过渡」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Android Academy 课中编写并运行代码吗?
能。每节 Android Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。