添加对内置和自定义预测式返回动画的支持

如果您已将您的应用迁移到新的系统返回 API,您可以选择加入预测式返回,以自动接收应用内动画并支持自定义过渡。

添加对内置应用内动画的支持

视频:预测式返回动画

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

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

请参阅GitHub 上的 Material 组件开发者指南,了解更多信息。

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

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

阅读有关支持预测式返回的更多信息。

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

您可以使用 Progress API 和自定义跨活动动画方法overrideActivityTransition创建自定义应用内属性动画和过渡。

使用 Progress API 添加自定义过渡

使用 AndroidX Activity 1.8.0-alpha01 或更高版本,您可以使用预测式返回 Progress API 为应用中的预测式返回手势开发自定义动画。在OnBackPressedCallback中,我们引入了handleOnBackProgressedhandleOnBackCancelledhandleOnBackStarted方法,用于在用户向后滑动时为对象设置动画。如果您需要比新的系统动画或 Material 组件动画提供的默认动画更自定义的内容,请使用这些方法。

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

将 Progress API 与 AndroidX Transitions 配合使用

在 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 Transitions,但不支持框架过渡。我们建议迁移到非框架过渡。
  • 预测式返回过渡在 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 的退出动画。