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

您可以使用 View 或 Compose 创建自定义应用内属性动画和转场效果、自定义跨 activity 动画,以及自定义跨 fragment 动画(带有预测性返回手势)。如需尝试 Compose 方式,请参阅添加对预测性返回动画的支持

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

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

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

将 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 转场效果支持预测性返回转换,但框架转场效果不支持。从框架过渡迁移,改用 Animator 和 AndroidX 过渡。
  • 搭载 Android 14 及更高版本的设备支持预测性返回转场效果,但不向后兼容。
  • 系统还支持使用 XML 场景创建的转场效果。在 handleOnBackStarted 中,将 TransitionSeekController 设置为 TransitionManager.createSeekController 的结果,而不是 controlDelayedTransition 的结果。

其他资源