沉浸模式 | 手势导航连载 (四)

做者 / Chris Banes, Android 开发者关系团队工程师android

本文是手势导航连载的第四篇文章,若是您但愿了解其余手势导航的话题,请查看本系列的其余文章:git

本文咱们将为你们介绍的是手势交互和冲突在全屏应用 (系统栏也被隐藏) 下的状况和注意事项。让咱们紧接上一篇文章,给你们讲讲流程图右侧的两种状况。github

△ 请点击图片放大查看
右侧的两个解决方案都是 Android 平台为应用提供的沉浸模式 (immersive mode)。那问题来了: 什么是沉浸模式?

什么是沉浸模式?

沉浸模式是一种让内容全屏呈现的方式,用来隐藏系统栏,从而确保应用拥有最大的屏幕空间。此外,它还提供了防误操做的功能 (好比意外使用手势离开应用),特别适合在游戏中采用。bash

沉浸模式分为两种:app

  1. 非粘性沉浸模式: 用户能够经过在系统栏上滑动来退出沉浸模式。ide

  2. 粘性沉浸模式: 用户能够经过在系统栏上滑动来暂时退出沉浸模式。在通过一小段时间后 (只有几秒) 会从新自动回到沉浸模式。ui

这两种模式都有两种状态:this

  1. 系统栏隐藏: 在此状态下,返回主屏幕手势和后退手势均被禁用。用户必须首先从边缘向内侧滑动才能让系统栏显示。google

  2. 系统栏显示: 在此状态下,返回主屏幕手势和后退手势能够正常工做。spa

如今,咱们已经了解了沉浸模式的基础知识,下面介绍这两种不一样模式的细节。

非粘性沉浸模式

你们在上面的流程图中可能已经看到,非粘性 (non-sticky) 沉浸模式很是适合须要全屏显示但不须要在屏幕边缘附近使用精确滑动手势的 UI。常见的例子包括全屏视频播放和照片浏览等。

就手势导航而言,非粘性沉浸模式与其在早期版本的 Android 上的工做方式一致。在 Android 10 或以上系统中运行时,应用可使用咱们在上一篇文章中介绍的手势区域排除 API 。在此模式下,不管系统栏是否可见,每一个边缘能排除的区域高度仍旧限制为 200dp。

若是您的应用正在使用非粘性沉浸模式,咱们建议您回顾一下第三篇文章,避免在屏幕边缘出现的视图与系统手势出现冲突。

粘性沉浸模式

粘性 (sticky) 沉浸模式适合那些强烈须要使用整个屏幕,并要求在整个屏幕区域内进行触摸输入的 UI。常见的例子是绘图应用,以及使用滑动操做的游戏。

咱们来看一下运行在 Android 10 上,且使用手势导航的 Markers 绘图应用:

如上图所示,一旦用户开始在屏幕边缘附近滑动 (绘制),就会触发后退手势,这会打断用户当前的操做。

使用粘性沉浸模式的应用会有很强的交互性,所以手势区域排除 API 的限制会被移除,但仅限于系统栏隐藏的时候。这意味着应用能够根据须要彻底占用屏幕左 / 右边缘。

可是,在系统栏可见时,系统则会忽略全部排除的手势区域,让用户能够返回,而不会受到来自应用的干扰。在粘性沉浸模式下,系统栏仅在短期内可见,所以不会影响应用的正常交互。

屏幕底部的主屏手势区域依旧会正常存在,是没法排除的 "强制" 手势区域。处于粘性沉浸模式的应用可能会占用两个垂直边缘的整个长度,所以屏幕底部的主手势区域多是用户呼出系统栏并退出应用的惟一方法。

接下来咱们来看一下绘图应用的改进版本,整个垂直边缘都被应用占用:

能够看到,用户如今能够在屏幕边缘附近自由绘制,后退手势不会再干扰他们。若是用户想要退出应用,则能够从屏幕底部向上滑动呼出系统栏,进行后退或返回主屏的操做。

在实现方面,此处使用的代码大致沿用自第三篇文章中的 "使用手势区域排除 API" 部分,不一样之处在于,咱们但愿视图可以知道它自身是否处于沉浸模式之中:

private val exclusionRects = ArrayList<Rect>()

override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
    super.onLayout(changed, left, top, right, bottom)
    if (changed && Build.VERSION.SDK_INT >= 29) {
        updateGestureExclusionRects()
    }
}

override fun onWindowSystemUiVisibilityChanged(visibility: Int) {
    super.onWindowSystemUiVisibilityChanged(visibility)

    // Update our gesture exclusions rects if we’re
    // running on Android 10+
    if (Build.VERSION.SDK_INT >= 29) {
        updateGestureExclusionRects()
    }
}

private fun updateGestureExclusionRects() {
    // If the navigation bar is hidden, let's exclude any vertical edges so // that the user can draw freely if ((windowSystemUiVisibility and SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0) { // Root window insets are null, which happens if this is called // before we're attached and laid out. Ignore the call for now.
        val rootWindowInsets = rootWindowInsets ?: return

        val gestureInsets = rootWindowInsets.systemGestureInsets

        exclusionRects.clear()
        // Add an exclusion rect for the left gesture edge
        exclusionRects += Rect(0, 0, gestureInsets.left, height)
        // Add an exclusion rect for the right gesture edge
        exclusionRects += Rect(width - gestureInsets.right, 0, width, height)

        systemGestureExclusionRects = exclusionRects
    } else {
        // If the navigation bar is showing, we don't want to exclude any edges. systemGestureExclusionRects = emptyList() } } 复制代码

您能够在 GitHub 上阅读 Makers 应用更新的完整代码。

总结对比: 非粘性与粘性

呼,一口气看到这里可能有点记不住。这里我为你们了提供一张表格,它总结出了非粘性和粘性沉浸模式之间的差别。

△ 请点击图片放大查看

继续深刻

如何处理手势交互中的冲突就讲到这里。我也但愿您已经对手势交互有了更深的理解,并将这些理解完美落实到应用的开发与更新中去。

本系列的第五篇文章 (也是最后一篇文章) 将介绍您可能会在应用中采用的一些常见 UI 模式,以及如何在手势导航中支持它们。

点击这里进一步了解 Android 手势导航

相关文章
相关标签/搜索