添加对预测性返回动画的支持

使用系统返回 API 时,您可以选择接收应用内动画并支持自定义转场效果。

视频:预测性返回动画

选择启用后,您的应用会显示“返回主屏幕”、跨 activity 和跨任务的动画。

您还可将 Material 组件依赖项升级到 v1.10.0 的 MDC Android,以接收 Material 组件动画,比如:

如需了解详情,请参阅 GitHub 上的 Material 组件开发者指南

该视频展示了使用 Android 的“设置”应用实现的跨 activity 和“返回主屏幕”预测性返回动画的简短示例。

  1. 在动画中,用户滑回先前的“设置”界面,这是一个跨 activity 动画示例。
  2. 现在,在上一个界面上,用户又开始再滑动一次,系统会显示主屏幕及壁纸的预览,即“返回主屏幕”动画的示例。
  3. 用户继续向右滑动,此时系统显示了窗口如何缩小为主屏幕上的图标的动画。
  4. 用户现已完全返回主屏幕。

详细了解如何添加对预测性返回手势的支持

添加自定义应用内转场效果和动画

您可以创建自定义应用内属性动画和转换、自定义跨 activity 动画,以及具有预测性返回手势的自定义跨 fragment 动画。

使用 Progress API 添加自定义转场效果

借助 AndroidX Activity 1.8.0-alpha01 或更高版本,您可以使用预测性返回进度 API 为应用中的预测性返回手势开发自定义动画。Progress API 有助于为视图添加动画效果,但在为 fragment 之间的过渡添加动画效果时受到限制。在 OnBackPressedCallback 中,我们引入了 handleOnBackProgressedhandleOnBackCancelledhandleOnBackStarted 方法,以便在用户滑回时为对象添加动画效果。如果您需要自定义除系统提供的默认动画或 Material 组件动画之外的其他动画,请使用这些方法。

我们希望大多数应用都使用向后兼容的 AndroidX API,但 OnBackAnimationCallback 接口中也有类似的平台 API 可用于在 Android 14 开发者预览版 1 及更高版本中进行测试。

将 Progress API 与 AndroidX 转场效果搭配使用

在 Android 14 及更高版本中,Progress API 可与 AndroidX Transitions 1.5.0-alpha01 或更高版本一起使用,以创建预测性返回转场效果。

  1. 使用 TransitionManager#controlDelayedTransition(而非 beginDelayedTransition)在用户滑回时播放转场效果。
  2. handleOnBackStarted 中创建转场效果。
  3. 通过将 currentFractionBackEvent.progress(公开用户已滑回的距离)相关联,在 handleOnBackProgressed 中使用返回事件播放转场效果。
  4. 当用户在 handleOnBackPressed 中提交返回手势后,完成转场。
  5. 最后,在 handleOnBackCancelled 中重置转场状态。

以下视频、Kotlin 代码和 XML 展示了使用 OnBackPressedCallback 实现的两个框之间的自定义转场效果:

    class MyFragment : Fragment() {

    val transitionSet = TransitionSet().apply {
        addTransition(Fade(Fade.MODE_OUT))
        addTransition(ChangeBounds())
        addTransition(Fade(Fade.MODE_IN))
    }
    ...
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        val callback = object : OnBackPressedCallback(enabled = false) {

            var controller: TransitionSeekController? = null

            @RequiresApi(34)
            override fun handleOnBackStarted(backEvent: BackEvent) {
                // Create the transition
                controller = TransitionManager.controlDelayedTransition(
                    binding.card,
                    transitionSet
                )
                changeTextVisibility(ShowText.SHORT)
            }

            @RequiresApi(34)
            override fun handleOnBackProgressed(backEvent: BackEvent) {
                // Play the transition as the user swipes back
                if (controller?.isReady == true) {
                    controller?.currentFraction = backEvent.progress
                }
            }

            override fun handleOnBackPressed() {
                // Finish playing the transition when the user commits back
                controller?.animateToEnd()
                this.isEnabled = false
            }

            @RequiresApi(34)
            override fun handleOnBackCancelled() {
                // If the user cancels the back gesture, reset the state
                transition(ShowText.LONG)
            }
        }

        binding.shortText.setOnClickListener {
            transition(ShowText.LONG)
            callback.isEnabled = true
        }

        this.requireActivity().onBackPressedDispatcher.addCallback(callback)
    }

    private fun transition(showText: ShowText) {
        TransitionManager.beginDelayedTransition(
            binding.card,
            transitionSet
        )
        changeTextVisibility(showText)
    }

    enum class ShowText { SHORT, LONG }
    private fun changeTextVisibility(showText: ShowText) {
        when (showText) {
            ShowText.SHORT -> {
                binding.shortText.isVisible = true
                binding.longText.isVisible = false
            }
            ShowText.LONG -> {
                binding.shortText.isVisible = false
                binding.longText.isVisible = true
            }
        }
    }
}
  
<?xml version="1.0" encoding="utf-8"?>
...
    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/card"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        ...>

        <TextView
            android:id="@+id/short_text"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            ... />

        <TextView
            android:id="@+id/long_text"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:visibility="gone"
            .../>

    </androidx.constraintlayout.widget.ConstraintLayout>

使用预测性返回转场效果时,请注意以下几点:

  • 使用 isSeekingSupported 检查转场效果是否支持预测性返回。
  • 替换 isSeekingSupported 以针对自定义转场效果返回 true。
  • 为每个动画创建一个控制器。
  • AndroidX 转场效果支持预测性返回转换,但框架转场效果不支持。我们建议不要使用框架转场效果。
  • 搭载 Android 14 及更高版本的设备支持预测性返回转场效果,但不向后兼容。
  • 系统还支持使用 XML 场景创建的转场效果。在 handleOnBackStarted 中,将 TransitionSeekController 设置为 TransitionManager.createSeekController 的结果,而不是 controlDelayedTransition 的结果。

在 Android 14 及更高版本上添加自定义 activity 转场效果

为确保自定义 activity 转换在 Android 14 及更高版本上支持预测性返回,您可以使用 overrideActivityTransition 而非 overridePendingTransition。这意味着,转换效果会在用户滑回时播放。

举个例子,假设在返回堆栈中 activity B 位于 activity A 之上。您可使用以下方式处理自定义 activity 动画:

  • 在 activity B 的 onCreate 方法中调用开始/结束转换效果。
  • 当用户导航到 activity B 时,使用 OVERRIDE_TRANSITION_OPEN。当用户滑回 activity A 时,使用 OVERRIDE_TRANSITION_CLOSE
  • 指定 OVERRIDE_TRANSITION_CLOSE 时,enterAnim 是 activity A 的进入动画,而 exitAnim 是 activity B 的退出动画。

添加对使用 fragment 实现预测性返回的支持

使用 fragment 实现预测性返回时,有两种方法可供选择。

使用现有 API

我们建议您使用现有的 API。这些 API 可让您从屏幕边缘滑动,以便通过手势操控 Animator 或 Androidx 过渡。手势是否超过阈值决定了手势已完成并返回到上一个 fragment,还是手势被取消并留在当前 fragment。如需了解详情,请参阅使用动画在 fragment 之间导航

请注意以下因素:

  • 导入 Transitions 1.5.0 或更高版本以及 Fragment 1.7.0 或更高版本。Fragment 中的许多预测性返回支持都依赖于 Transitions 能够让用户用拖动条查看动画的功能,而此功能只能在 Transitions 1.5.0 或更高版本中实现。
  • 将 Fragment 与 FragmentManagerNavigation 组件一起使用,以处理返回堆栈。如果您自行管理返回堆栈,则不支持预测性返回。
  • 部分库支持预测性返回。请查看相关文档进行确认。
  • 支持 Animator 类和 AndroidX Transition 库。
  • 不支持 Animation 类和框架 Transition 库。
  • 预测性动画仅适用于搭载 Android 14 或更高版本的设备。

在以下情况下使用预测性返回交叉 fragment:

某些 Material 动作支持从 1.12.02-alpha02 或更高版本开始的预测性返回,包括 MaterialFadeThroughMaterialSharedAxisMaterialFade。请注意,MaterialContainerTransform 不支持预测性返回。

使用回调

您可以使用回调创建跨 fragment 过渡,但在使用回调时,存在一个已知的限制,即用户滑回时无法看到上一个 fragment。如需创建与预测性返回设计指南对应的跨 fragment 共享元素过渡,请执行以下操作:

创建 OnBackPressedCallback。在 handleOnBackProgressed 中,缩放和移动 Fragment。然后从返回堆栈中弹出。接下来,在回调之外使用 setSharedElementReturnTransition 运行共享元素过渡。

如需了解详情,请参阅 GitHub 上的代码示例

要求

请参阅下表了解开发者选项、targetSdkVersioncompileSdkVersion、设备版本、依赖项、清单标志和 fragment 标志所控制的内容。第一个表格展示了代码要求。

类别 动画 compileSdk targetSdk android:enableOnBackInvokedCallback 依赖项
系统动画 返回主屏幕 33 任意 TRUE
跨 activity 34 任意 TRUE
跨任务 34 任意 TRUE
平台 自定义跨 activity 34 任意 TRUE
Progress API 平台 34 任意 TRUE
Material 组件 底部动作条 34 任意 TRUE Material 组件 1.10.0
侧边动作条 34 任意 TRUE Material 组件 1.10.0
抽屉式导航栏 34 任意 TRUE Material 组件 1.10.0
搜索 34 任意 TRUE Material 组件 1.10.0
Jetpack 动画 自定义 AndroidX 跨 fragment 34 任意 TRUE AndroidX Fragment 1.7
自定义 AndroidX 转换 34 任意 TRUE AndroidX Transition 1.5
Progress API Jetpack 34 任意 TRUE AndroidX Activity 1.8

第二个表格展示了允许用户看到动画所需满足的要求。

类别 动画 开发者选项是否启用 设备版本
系统动画 返回主屏幕 TRUE 33
跨 activity TRUE 34
跨任务 TRUE 34
平台 自定义跨 activity TRUE 34
Progress API 平台 FALSE 34
Material 组件 底部动作条 FALSE 34
侧边动作条 FALSE 34
抽屉式导航栏 FALSE 34
搜索 FALSE 34
Jetpack 动画 自定义 AndroidX 跨 fragment FALSE 34
自定义 AndroidX 转换 FALSE 34
Progress API Jetpack FALSE 34

其他资源