添加对预测后退动画的支持

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

视频:预测性返回动画

选择加入后,您的应用将显示返回主页、跨活动和跨任务的动画。

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

有关更多信息,请参阅GitHub 上的 Material 组件开发者指南

该视频简要演示了 Android 设置应用中跨活动和返回主页的预测性返回动画的示例。

  1. 在动画中,用户向后滑动以返回到之前的设置屏幕 - 这是跨活动动画的示例。
  2. 现在,在之前的屏幕上,用户开始第二次向后滑动,显示了带有壁纸的主屏幕的预览 - 这是返回主页动画的示例。
  3. 用户继续向右滑动,显示了窗口缩小到主屏幕上的图标的动画。
  4. 用户现已完全返回主屏幕。

了解有关如何添加对预测性返回手势的支持的更多信息。

添加自定义应用内过渡和动画

您可以创建自定义应用内属性动画和过渡、自定义跨活动动画和带有预测性返回手势的自定义跨片段动画。

使用进度 API 添加自定义过渡

从 AndroidX Activity 1.8.0-alpha01 或更高版本开始,您可以使用预测性后退进度 API 来开发应用中预测性后退手势的自定义动画。进度 API 有助于为视图制作动画,但在为片段之间的转换制作动画时存在局限性。在 OnBackPressedCallback 中,我们引入了 handleOnBackProgressedhandleOnBackCancelledhandleOnBackStarted 方法,以便在用户向后滑动时为对象制作动画。如果您需要自定义系统提供的默认动画或 Material 组件动画以外的其他内容,请使用这些方法。

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

将进度 API 与 AndroidX Transitions 结合使用

可以在 Android 14 及更高版本上将进度 API 与 AndroidX Transitions 1.5.0-alpha01 或更高版本结合使用,以创建预测性后退转换。

  1. 使用 TransitionManager#controlDelayedTransition 而不是 beginDelayedTransition,以便在用户向后滑动时播放转换。
  2. handleOnBackStarted 中创建转换。
  3. 通过将 currentFractionBackEvent.progress 相关联来播放 handleOnBackProgressed 中的转换,BackEvent.progress 会显示用户向后滑动的距离。
  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 及更高版本上添加自定义活动转换

要确保自定义活动转换在 Android 14 及更高版本上支持预测性后退,可以使用 overrideActivityTransition 而不是 overridePendingTransition。这意味着转换动画在用户向后滑动时播放。

为了提供一个示例来说明这将如何工作,想象一下这样一种情况:活动 B 在回退堆栈中位于活动 A 的顶部。您将以以下方式处理自定义活动动画

  • 在活动 B 的 onCreate 方法中调用打开或关闭转换。
  • 当用户导航到活动 B 时,使用 OVERRIDE_TRANSITION_OPEN。当用户滑动以导航回活动 A 时,使用 OVERRIDE_TRANSITION_CLOSE
  • 在指定 OVERRIDE_TRANSITION_CLOSE 时,enterAnim 是活动 A 的进入动画,exitAnim 是活动 B 的退出动画。

添加对使用片段进行预测性后退的支持

在使用片段实现预测性后退时,有两种方法。

使用现有 API

我们建议您使用现有 API。这些 API 允许您从屏幕边缘滑动以使用手势操作 Animator 或 Androidx 转换。无论您是否将手势移过阈值,都决定了它是否完成并返回到上一个片段,或者它是否取消并停留在当前片段。有关详细信息,请参阅 使用动画在片段之间导航

请牢记以下因素

  • 导入 Transitions 1.5.0 或更高版本以及 Fragments 1.7.0 或更高版本。片段中预测性后退支持的很大一部分依赖于 Transitions 能够搜索动画,而这只有在 Transitions 1.5.0 或更高版本中才可能实现。
  • 使用片段(使用 FragmentManager导航组件)来处理回退堆栈。如果您自行管理回退堆栈,则不支持预测性后退。
  • 某些库包含预测性后退支持。请检查文档以确保。
  • 支持 Animator 类和 AndroidX Transition 库。
  • 不支持 Animation 类和框架 Transition 库。
  • 预测动画仅在运行 Android 14 或更高版本的设备上有效。

在以下情况下使用跨片段的预测性后退

1.12.02-alpha02 或更高版本开始,一些 Material 动作 支持预测性后退,包括 MaterialFadeThroughMaterialSharedAxisMaterialFade。请注意,MaterialContainerTransform 不支持预测性后退。

使用回调

您可以使用回调创建跨片段转换,但是使用回调时存在一个已知限制,即用户在向后滑动时无法看到上一个片段。要创建与预测性后退 设计指南 相对应的跨片段共享元素转换,请执行以下操作

创建一个 OnBackPressedCallback。在 handleOnBackProgressed 中,缩放和移动片段。然后从回退堆栈中弹出。接下来,使用 setSharedElementReturnTransition 在回调外部运行共享元素转换。

有关详细信息,请参阅 GitHub 上的 代码示例

要求

使用以下表格了解开发者选项、targetSdkVersioncompileSdkVersion、设备版本、依赖项、清单标志和片段标志控制的内容。第一个表格指的是代码要求。

类别 动画 compileSdk targetSdk android:enableOnBackInvokedCallback 依赖项
系统动画 返回首页 33 任何 TRUE
跨活动 34 任何 TRUE
跨任务 34 任何 TRUE
平台 自定义跨活动 34 任何 TRUE
进度 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 跨片段 34 任何 TRUE AndroidX Fragment 1.7
自定义 AndroidX Transitions 34 任何 TRUE AndroidX Transition 1.5
进度 API Jetpack 34 任何 TRUE AndroidX Activity 1.8

以下表格指的是允许用户查看动画的要求。

类别 动画 启用的开发者选项 设备版本
系统动画 返回首页 TRUE 33
跨活动 TRUE 34
跨任务 TRUE 34
平台 自定义跨活动 TRUE 34
进度 API 平台 FALSE 34
Material 组件 底部工作表 FALSE 34
侧边工作表 FALSE 34
导航抽屉 FALSE 34
搜索 FALSE 34
Jetpack 动画 自定义 AndroidX 跨片段 FALSE 34
自定义 AndroidX Transitions FALSE 34
进度 API Jetpack FALSE 34

其他资源