添加对预测性返回手势的支持

图 1. 预测性返回手势在手机上的视觉效果和风格模拟

预测性返回是一项手势导航功能,可让用户预览返回滑动操作将其带到的位置。

例如,使用返回手势可以在应用后面显示主屏幕的动画预览,如图 1 中的模型所示。

从 Android 15 开始,预测性返回动画的开发者选项将不再可用。现在,对于完全或在 activity 级别选择启用预测性返回手势的应用,系统会显示“返回主屏幕”“跨任务”和“跨 activity”等系统动画。

您可以测试此返回主屏幕动画(如本页下一部分所述)。

如需支持预测性返回手势,您需要使用向后兼容的 OnBackPressedCallback AppCompat 1.6.0-alpha05 (AndroidX) 或更高版本的 API 或使用新的 OnBackInvokedCallback 平台 API 更新应用。大多数应用都使用向后兼容的 AndroidX API。

此更新提供了一种迁移路径,可正确拦截返回导航,包括在 KeyEvent.KEYCODE_BACK 和任何使用 onBackPressed 方法的类(例如 ActivityDialog)中,将返回拦截替换成新的系统 Back API。

Codelab 和 Google I/O 大会视频

除了参考本页的文档外,您还可以试用我们的 Codelab, 其中提供了使用 AndroidX Activity API 处理预测性返回手势的常见 WebView 用例实现。

您还可以观看我们的 Google I/O 视频,其中介绍了实现 AndroidX 和平台 API 的其他示例。

更新使用默认返回导航的应用

如果您的应用未实现任何自定义返回行为(换言之,由系统处理返回导航),则更新应用以支持此功能非常简单。按照本指南中的说明选择启用此功能即可。

如果您的应用使用 fragment 或 Navigation 组件,也请升级到 AndroidX Activity 1.6.0-alpha05 或更高版本。

更新使用自定义返回导航的应用

如果您的应用实现了自定义返回行为,则会有不同的迁移路径,具体取决于它是否使用了 AndroidX 以及它如何处理返回导航。

您的应用是否使用了 AndroidX 您的应用会如何处理返回导航 推荐的迁移路径(此页面上的链接)
AndroidX APIs 迁移现有的 AndroidX 返回实现
不受支持的平台 API 将包含不受支持的返回导航 API 的 AndroidX 应用迁移至 AndroidX API
不受支持的平台 API,可以迁移 将使用不受支持的返回导航 API 的应用迁移至平台 API
不受支持的平台 API,但无法迁移 推迟选择启用此功能,直到此功能成为必要功能为止

迁移 AndroidX 返回导航实现

此用例最常见(也最推荐)。它适用于使用 OnBackPressedDispatcher 实现自定义手势导航处理功能的新应用或现有应用,如提供自定义返回导航中所述。

如果您的应用属于此类别,请按照以下步骤添加对预测性返回手势的支持:

  1. 为了确保已在使用 OnBackPressedDispatcher API 的 API(例如 fragment 和 Navigation 组件)能够与预测性返回手势完美配合,请升级到 AndroidX Activity 1.6.0-alpha05

    // In your build.gradle file:
    dependencies {
    
    // Add this in addition to your other dependencies
    implementation "androidx.activity:activity:1.6.0-alpha05"
    
  2. 按照本页面中的说明选择启用预测性返回手势

将包含不受支持的返回导航 API 的 AndroidX 应用迁移至 AndroidX API

如果您的应用使用 AndroidX 库,但会实现或引用不受支持的返回导航 API,则需要改用 AndroidX API 来支持新行为。

如需将不受支持的 API 迁移到 AndroidX API,请执行以下操作:

  1. 通过实现 OnBackPressedCallback 将系统返回导航处理逻辑迁移到 AndroidX 的 OnBackPressedDispatcher。如需获得详细指导,请参阅提供自定义返回导航

  2. 当已为停止拦截返回手势做好准备时,停用 OnBackPressedCallback

  3. 停止通过 OnBackPressedKeyEvent.KEYCODE_BACK 拦截返回事件。

  4. 请务必升级到 AndroidX Activity 1.6.0-alpha05

    // In your build.gradle file:
    dependencies {
    
    // Add this in addition to your other dependencies
    implementation "androidx.activity:activity:1.6.0-alpha05"
    
  5. 成功迁移应用后,请选择启用预测性返回手势(如本页所述),以查看“返回主屏幕”系统动画。

将使用不受支持的返回导航 API 的应用迁移至平台 API

如果您的应用无法使用 AndroidX 库,而是使用不受支持的 API 实现或引用自定义返回导航,您必须迁移到 OnBackInvokedCallback 平台 API。

完成以下步骤,将不受支持的 API 迁移到平台 API:

  1. 在搭载 Android 13 或更高版本的设备上使用新的 OnBackInvokedCallback API,并在搭载 Android 12 或更低版本的设备上依赖不受支持的 API。

  2. OnBackInvokedCallback 中向 onBackInvokedDispatcher 注册您的自定义返回逻辑。这样可防止结束当前的 activity,让回调有机会在用户完成系统返回导航后响应返回操作。

  3. 当已为停止拦截返回手势做好准备时,取消注册 OnBackInvokedCallback。否则,用户在使用系统返回导航时,可能会遇到不希望出现的行为,例如在视图之间“卡住”并被强制退出应用。

    以下示例展示了如何从 onBackPressed 迁移出逻辑:

    Kotlin

    @Override
    fun onCreate() {
        if (BuildCompat.isAtLeastT()) {
            onBackInvokedDispatcher.registerOnBackInvokedCallback(
                OnBackInvokedDispatcher.PRIORITY_DEFAULT
            ) {
                /**
                 * onBackPressed logic goes here. For instance:
                 * Prevents closing the app to go home screen when in the
                 * middle of entering data to a form
                 * or from accidentally leaving a fragment with a WebView in it
                 *
                 * Unregistering the callback to stop intercepting the back gesture:
                 * When the user transitions to the topmost screen (activity, fragment)
                 * in the BackStack, unregister the callback by using
                 * OnBackInvokeDispatcher.unregisterOnBackInvokedCallback
                 * (https://developer.android.com/reference/kotlin/android/window/OnBackInvokedDispatcher#unregisteronbackinvokedcallback)
                 */
            }
        }
    }

    Java

    @Override
    void onCreate() {
      if (BuildCompat.isAtLeastT()) {
        getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
            OnBackInvokedDispatcher.PRIORITY_DEFAULT,
            () -> {
              /**
               * onBackPressed logic goes here - For instance:
               * Prevents closing the app to go home screen when in the
               * middle of entering data to a form
               * or from accidentally leaving a fragment with a WebView in it
               *
               * Unregistering the callback to stop intercepting the back gesture:
               * When the user transitions to the topmost screen (activity, fragment)
               * in the BackStack, unregister the callback by using
               * OnBackInvokeDispatcher.unregisterOnBackInvokedCallback
               * (https://developer.android.com/reference/kotlin/android/view/OnBackInvokedDispatcher#unregisteronbackinvokedcallback)
               */
            }
        );
      }
    }
  4. 对于 Android 13 及更高版本,停止通过 OnBackPressedKeyEvent.KEYCODE_BACK 拦截返回事件。

  5. 成功迁移应用后,请选择启用预测性返回手势(如本页所述),以使 OnBackInvokedCallback 生效。

您可以向 PRIORITY_DEFAULTPRIORITY_OVERLAY 注册 OnBackInvokedCallback,后者在类似的 AndroidX OnBackPressedCallback 中不可用。在某些情况下,向 PRIORITY_OVERLAY 注册回调会很有帮助。

当您从 onKeyPreIme() 迁移并且回调需要接收返回手势而不是打开的 IME 时,就需要这样做。IME 在打开时会向 PRIORITY_DEFAULT 注册回调。请向 PRIORITY_OVERLAY 注册回调,以确保 OnBackInvokedDispatcher 将返回手势分派给回调而不是打开的 IME。

选择启用预测性返回手势

根据具体情况确定如何更新应用后,就可以选择支持预测性返回手势。

如需选择启用预测性返回手势,请在 AndroidManifest.xml<application> 标记中将 android:enableOnBackInvokedCallback 标志设置为 true

<application
    ...
    android:enableOnBackInvokedCallback="true"
    ... >
...
</application>

如果您不指定值,则默认为 false 并执行以下操作:

  • 停用预测性返回手势系统动画。
  • 忽略 OnBackInvokedCallback,但 OnBackPressedCallback 调用将继续有效。

在 activity 级别选择启用

从 Android 14 开始,您可以使用 android:enableOnBackInvokedCallback 标志在 activity 级别选择启用预测性系统动画。此行为有助于更轻松地管理将大型多 activity 应用迁移到预测性返回手势的过程。在 Android 15 中,预测性返回功能不再位于开发者选项背后。应用可以选择完全启用预测性返回,也可以在 activity 级别启用。

以下代码展示了使用 enableOnBackInvokedCallbackMainActivity 启用“返回主屏幕”系统动画的示例:

<manifest ...>
    <application . . .

        android:enableOnBackInvokedCallback="false">

        <activity
            android:name=".MainActivity"
            android:enableOnBackInvokedCallback="true"
            ...
        </activity>
        <activity
            android:name=".SecondActivity"
            android:enableOnBackInvokedCallback="false"
            ...
        </activity>
    </application>
</manifest>

在前面的示例中,为 ".SecondActivity" 设置 android:enableOnBackInvokedCallback=true 会启用跨 activity 系统动画。

在使用 android:enableOnBackInvokedCallback 标志时,请注意以下事项:

  • 设置 android:enableOnBackInvokedCallback=false 会在 activity 级别或应用级别关闭预测性返回动画(具体取决于您在哪个级别设置了标记),并指示系统忽略对 OnBackInvokedCallback 平台 API 的调用。 不过,对 OnBackPressedCallback 的调用会继续运行,因为 OnBackPressedCallback 向后兼容,并会调用 Android 13 之前的版本不支持的 onBackPressed API。
  • 在应用级别设置 enableOnBackInvokedCallback 标志会为应用中的所有 activity 建立默认值。您可以在 activity 级别设置标志来替换每个 activity 的默认值,如前面的代码示例所示。

回调最佳实践

使用 BackHandler(适用于 Compose)、OnBackPressedCallbackOnBackInvokedCallback 等受支持的系统返回回调时,请遵循以下最佳实践。

确定用于启用和停用每个回调的界面状态

界面状态是描述界面的属性。我们建议您遵循以下简要步骤。

  1. 确定用于启用和停用每个回调的界面状态。

  2. 使用可观察数据容器类型(例如 StateFlow 或 Compose State)定义该状态,并在状态更改时启用或停用回调。

如果您的应用之前已将返回逻辑与条件语句相关联,这可能表示您是在返回事件发生后才做出回应。避免在较新的回调中使用此模式。如果可能,请将回调移出条件语句,并改为将回调与可观察数据容器类型关联起来。

对界面逻辑使用系统返回回调

界面逻辑决定着如何显示界面。您可以使用系统返回回调运行界面逻辑,例如显示弹出式窗口或运行动画。

如果您的应用启用了系统返回回调,则预测性动画不会运行,并且您必须处理返回事件。请勿创建仅用于运行非界面逻辑的回调。

例如,如果拦截返回事件只是为了记录日志,请改为在 activity 或 fragment 生命周期内进行记录。

  • 在 activity 到 activity 或 fragment 到 activity 的情况下,如果 onDestroy 中的 isFinishingtrue,请在 activity 生命周期内进行记录。
  • 在 fragment 到 fragment 的情况下,如果 onDestroy 中的 isRemoving 为 true,请在 fragment 的视图生命周期中进行记录。或者,使用 FragmentManager.OnBackStackChangedListener 中的 onBackStackChangeStartedonBackStackChangeCommitted 方法进行记录。

对于 Compose 用例,请在与 Compose 目的地关联的 ViewModelonCleared() 回调中记录日志。这是了解 Compose 目的地何时从返回堆栈中弹出并被销毁的最佳信号。

创建单一责任回调

您可以向调度程序添加多个回调。这些回调将添加到堆栈中,其中最近添加的已启用回调会处理下一个返回手势,且每个返回手势对应一个回调。

如果回调只有一项职责,则更容易管理其启用状态。例如:

堆栈中回调的排序。
图 2. 回调堆栈图。

图 2 展示了如何在堆栈中设置多个回调,每个回调负责一项任务。只有当堆栈中位于其上方的回调处于停用状态时,回调才会运行。在此示例中,当用户在表单中输入数据时,系统会启用“确定要执行此操作吗?”回调;否则,系统会停用该回调。当用户滑回退出表单时,回调会打开一个确认对话框。

其他回调可以包括支持预测性返回的 Material 组件、使用 Progress API 的 AndroidX 转场效果,或其他自定义回调。

如果上述回调已停用且此 FragmentManager 的返回堆栈不为空(其中 childFragmentManager 附加在 fragment 中),则会运行 childFragmentManager 的回调。在此示例中,此内部回调已停用。

同样,如果上述回调已停用且其堆栈不为空,则 supportFragmentManager 的内部回调也会运行。无论是使用 FragmentManager 还是 NavigationComponent 进行导航,此行为都是一致的,因为 NavigationComponent 依赖于 FragmentManager。在此示例中,如果用户未在表单中输入文本,导致“确定要执行此操作吗?”回调被停用,则系统会运行此回调。

最后,super.onBackPressed() 是系统级回调,如果上述回调被停用,系统会再次运行该回调。为了触发“返回主屏幕”“跨 activity”和“跨任务”等系统动画,supportFragmentManager 的返回堆栈必须为空,以便停用其内部回调。

测试预测性返回手势动画

如果您仍在使用 Android 13 或 Android 14,则可以测试图 1 中所示的返回主屏幕动画。

如需测试此动画,请完成以下步骤:

  1. 在您的设备上,依次转到设置 > 系统 > 开发者选项

  2. 选择预测性返回手势动画

  3. 启动更新后的应用,然后执行返回手势,看看实际运行效果。