0Pricing
Android Academy · 课时

动画与过渡

使用 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 反馈 — 无需本地设置。

此课程中的所有课时

  1. Material Design 基础
  2. 主题与样式
  3. 自定义视图
  4. 动画与过渡
  5. 底部导航与标签页
← 返回 Android Academy