确保与手势导航兼容

从 Android 10(API 级别 29)开始,Android 系统支持完全基于手势的导航。为确保应用与此功能兼容,应用开发者应完成以下两项任务:

  • 将应用内容扩展到屏幕边缘。
  • 处理存在冲突的应用手势。

全屏应用内容

为了充分利用浮动导航栏提供的额外屏幕空间,您需要对应用做出一些更改。

设置透明系统栏

您可以通过在主题背景中设置以下值来实现这一点:

<!-- values-29/themes.xml: -->

    <style name="AppTheme" parent="...">
        <item name="android:navigationBarColor">@android:color/transparent</item>

        <!-- Optional, but recommended for full edge-to-edge rendering -->
        <item name="android:statusBarColor">@android:color/transparent</item>
    </style>
    
已启用按钮的透明导航栏。

或者,您也可以通过使用 Window.setNavigationBarColor()Window.setStatusBarColor() 来动态完成这一设置。

当设备设置为使用手势导航,并且应用导航栏采用透明背景时,系统会根据其后面内容的颜色自动更新句柄颜色。不过,当用户处于 2 个按钮或 3 个按钮的导航模式时,这些按钮不会更改颜色。系统会改用半透明背景,让这些按钮保持可见状态。不过,仅当应用适配 API 级别 29 或更高版本时,系统才能执行此操作。

设置界面可见度标记

为了能够将您的视图放置到屏幕边缘,您的应用必须告知系统它可以处理此类视图。为此,您可以使用 View.setSystemUiVisibility() 来设置以下标记:

Kotlin

    view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
            or View.SYSTEM_UI_FLAG_LAYOUT_STABLE)
    

Java

    view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
            | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
    

所有这些标记可以告知系统应全屏放置应用的视图,就好像导航栏和状态栏不存在一样。对于其他全屏事件,您还可以设置 SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN,以便将状态栏移到后侧。

如果您使用的是自动处理状态栏的视图类(如 CoordinatorLayoutDrawerLayout),SYSTEM_UI_FLAG_LAYOUT_STABLESYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 标记可能已设置。此外,如果您使用 setSystemUiVisibility() 设置其他标记(如 SYSTEM_UI_FLAG_IMMERSIVE),应注意这些其他标记不会覆盖上述标记。

即使您的应用使用的是全屏视图,系统仍会使用 WindowInsets API 指明系统栏的位置。

手动占用边衬区

如果您的应用使用的是自定义视图层次结构,您可能需要手动占用系统窗口边衬区。通常,您可以通过实现 OnApplyWindowInsetsListener 接口做到这一点:

Kotlin

    view.setOnApplyWindowInsetsListener() {v, insets ->
        insets.consumeSystemWindowInsets()
    

Java

    view.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
        @Override
        public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
            // 1. Move views on top edge down by insets.getSystemWindowInsetTop()
            // 2. Move views on bottom edge up by insets.getSystemWindowInsetBottom()
            // 3. Also check getSystemWindowInsetLeft/Right(), such as for landscape
            // orientations
            return insets.consumeSystemWindowInsets();
        }
    });
    

WindowInsets 通过 getSystemWindowInsets() 为所有系统栏提供常规可视边衬区。此外,Android 10 会将以下方法添加到 WindowInsets

处理存在冲突的应用手势

手势导航模式可能会与应用开发者以前所用的手势冲突。因此,您可能需要调整应用界面。

与返回手势冲突

新的返回系统手势是从屏幕左侧或右侧边缘向内滑动。这可能会干扰这些区域中的应用导航元素。如需保持位于屏幕左侧和右侧边缘的元素的功能,您需要告知系统哪些区域需要接收轻触输入,从而选择性地停用返回手势。为此,您可以将 List<Rect> 传递给 Android 10 中引入的 View.setSystemGestureExclusionRects() API。从 androidx.core:core:1.1.0-dev01 开始,ViewCompat 中也提供这种方法。

例如:

Kotlin

    var exclusionRects = listOf(rect1, rect2, rect3)

    fun onLayout(
            changedCanvas: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
      // Update rect bounds and the exclusionRects list
      setSystemGestureExclusionRects(exclusionRects)
    }

    fun onDraw(canvas: Canvas) {
      // Update rect bounds and the exclusionRects list
      setSystemGestureExclusionRects(exclusionRects)
    }
    

Java

    List<Rect> exclusionRects;

    public void onLayout(
            boolean changedCanvas, int left, int top, int right, int bottom) {
        // Update rect bounds and the exclusionRects list
        setSystemGestureExclusionRects(exclusionRects);
    }

    public void onDraw(Canvas canvas) {
        // Update rect bounds and the exclusionRects list
        setSystemGestureExclusionRects(exclusionRects);
    }
    

与主屏幕/快速切换手势存在冲突

新的主屏幕/快速切换系统手势都涉及在屏幕底部导航栏以前占用的空间内滑动。应用无法像停用返回手势一样停用这些手势。

为了缓解这个问题,Android 10 引入了 WindowInsets.getMandatorySystemGestureInsets() API,它会告知应用触摸识别阈值。

游戏和其他非视图型应用

游戏和其他不含视图层次结构的应用通常要求用户在系统手势区域附近执行滑动操作。在这些情况下,游戏可以使用 Window.setSystemGestureExclusionRects() 排除与系统手势预留区域重叠的区域。游戏应确保仅在必要时(例如在玩游戏过程中)排除这些区域。

如果游戏要求用户在主屏幕手势区域附近滑动,应用可以请求以沉浸模式布局。这会在用户与游戏交互时停用系统手势,但允许用户通过从屏幕底部滑动来重新启用系统手势。

其他资源

如需详细了解手势导航,请参阅以下其他资源。

博文

视频