在启动高级和基础的 Android开发课程之前,准备了本文的翻译。
这是有关手势控制的系列文章中的第三篇。如果您错过了第一部分和第二部分的翻译内容,则可以熟悉一下它们。在之前的文章中,我们讨论了从边缘到边缘填充屏幕空间的主题。在本文中,我们将研究如何处理应用程序的手势与Android 10中的新系统手势之间发生的任何冲突。手势冲突是什么意思?让我们来看一个例子。假设我们有一个音乐播放器,允许用户通过拖放来滚动浏览当前歌曲SeekBar
。
不幸的是,它SeekBar
太靠近返回主屏幕的手势区域,因此开始快速切换到上一个应用程序(QuickSwitch)的手势给用户带来了不便。在手势区域所在的屏幕的任何边缘上都可能发生相同的情况。有许多可能导致冲突的示例,例如:导航抽屉(DrawerLayout
),轮播(ViewPager),滑块(SeekBar
),在列表中滑动。这就带来了一个问题:“我们如何解决这个问题?”为简化此问题,我们创建了一个流程图,可根据情况为您提供答案。
您可以在此处找到流程图的PDF版本。我希望这些问题不需要解释,但以防万一,我将逐一讨论这些问题:1.应用程序是否需要隐藏导航栏和状态栏?
第一个问题是应用程序的主要用例是否需要隐藏导航和/或状态栏。通过隐藏,我们意味着这些系统面板根本不可见。这并不意味着您已经在应用程序中实现了“边缘到边缘”或类似的概念。对这个问题回答“是”的可能原因:应对此问题回答“是”的应用程序的常见示例是游戏,视频播放器,照片查看器,绘图应用程序。2.使用UI的主要场景涉及在手势区域内/周围滑动?
这个问题可以找出您的UI是否在用户必须滑动的手势区域(“后退”和“家”)中/旁边包含任何元素。游戏通常会说“是”,原因是- 屏幕上的控件通常位于屏幕的左/右边缘和底部附近。
- 在某些游戏中,您需要在屏幕上任何位置上的元素上滑动。
除游戏外,UI的常见示例包括:- UI裁剪照片,其中可拖动框架也在屏幕的左右边缘附近。
- 用户可以在覆盖整个屏幕的画布上绘图的绘图应用程序。
3.手势区域中/周围经常使用的视图?
我希望这是一个相当简单的问题。这也包括视图,这些视图覆盖手势的区域,然后扩展到屏幕的很大一部分,例如DrawerLayout
大屏幕ViewPager
。4.视图涉及滑动/拖动?
我们稍微改变了策略,开始研究个人观点。对于您对第三个问题持肯定态度的任何观点,我们作一点澄清:用户是否应该滑动/拖动它?在许多示例中,您必须回答“是»:SeekBars
,BottomSheet甚至是PopupMenu
(必须拖动才能打开)。5.视图是否完全/大致位于手势区域下?
基于第四个问题,我们现在阐明视图是完全还是主要位于手势区域。如果您的视图位于这样的可滚动容器中RecyclerView
,请以不同的方式思考此问题:完全/基本上扩展的视图是否在所有滚动位置的手势区域下都属于?如果用户可以从手势区域下方滚动视图,则您无需执行任何操作。在上图中,您可能会注意到轮播全屏(ViewPager
)作为否定答案的示例,并且想知道为什么不需要处理这种情况。这是由于以下事实:20dp
与视图的宽度相比,左/右手势的区域在宽度上相对较小(默认值:每个)。典型纵向屏幕上手机屏幕的宽度约为360dp,留下了320dp的可用空间,用户没有困难(这几乎是屏幕的90%)。即使具有内部边距/凹痕,用户仍可以舒适地滚动浏览转盘。6.视图边框是否与任何必需的手势区域重叠?
最后一个问题阐明了视图是否在任何必需的手势区域下。如果您回想起上一篇文章,您会记得系统手势的强制性区域是屏幕手势始终优先的屏幕区域。在Android 10中,只有一个强制性手势区域位于屏幕底部,该区域允许用户返回家中或打开其最新应用程序。这可能会在平台的将来版本中发生变化,但是现在我们只需要使用屏幕底部的视图即可。典型示例有:- 无模式BottomSheet,因为它们倾向于折叠成屏幕底部的一个小的拖放视图。
- 屏幕底部的水平滚动轮播,例如带有贴纸的界面。
既然我们已经解决了问题,我们希望您能找到其中一种解决方案,因此让我们更详细地研究每个解决方案。没有冲突要处理
让我们从最简单的“解决方案”开始,什么都不做!当然,也许仍有进行优化的空间(请参阅下面的部分),但是幸运的是,在打开手势导航模式的情况下使用该应用程序不会出现严重问题。如果日程安排将您带到这里,但您仍然觉得有问题,请告诉我们。也许我们错过了一些东西。从手势区域移动视图
正如我们从上一篇文章中学到的那样,存在一些实例来告诉您的应用程序系统手势区域在屏幕上的位置。解决手势冲突的一种方法是从手势区域移动所有冲突的视图。这对于屏幕底部的视图尤为重要,因为该区域是强制手势区域,并且应用程序无法在此处使用手势排除API。让我们再来看一个例子。我们上面显示了一个音乐播放器界面。它包含SeekBar
位于屏幕底部的,允许用户滚动浏览歌曲。
屏幕底部带有SeekBar的UI音乐播放器但是当用户尝试跳过歌曲时,会发生这种情况:
记录与SeekBar冲突的系统手势这是因为手势的下部区域与SeekBar重叠,因此向后倾斜手势优先。这是手势区域的可视化:
一个简单的解决方案
这里最简单的解决方案是添加一个额外的缩进/边距,以使SeekBar从手势区域向上移动。这样的事情:
如果在此示例中拖动SeekBar,您将看到我们不再激活归位手势:
SeekBar不再与较低的系统手势冲突。要实现此功能,我们需要使用API 29和Jetpack Core库 v1.2.0(当前为alpha)中可用的新系统手势插入。在示例中,我们增加了底部缩进量以匹配较低手势inset的值:SeekBar
ViewCompat.setOnApplyWindowInsetsListener(seekBar) { view, insets ->
view.updatePadding(
bottom = insets.systemGestureInsets.bottom
)
insets
}
如果您有兴趣学习如何使其更易于使用WindowInsets
,可以阅读有关该主题的另一篇文章:WindowInsets-侦听器布局进一步行动
此时,您可以确定该工作已经完成,对于某些布局,这很可能是最终解决方案。但是在我们的示例中,用户界面在视觉上逐渐退缩,下方有很多丢失的空间SeekBar
。因此,除了简单地向上移动视图之外,我们还可以重新设计布局以避免丢失空间:
SeekBar
移至回放面板的顶部。在这里,我们移到SeekBar
了回放面板的顶部,完全在手势区域之外。这意味着我们不再需要人为地增加面板的高度来容纳SeekBar
。, . « ».API
在上一篇文章中,我们提到“应用程序可以排除屏幕某些部分的系统手势。”应用程序使用最早在Android 10中出现的手势排除API来执行此操作。系统提供了两种不同的功能来排除手势区域:View.setSystemGestureExclusionRects()
和Window.setSystemGestureExclusionRects()
。您应该使用什么取决于应用程序:如果您使用Android View
,则系统首选View API,否则请使用该Window
API。两种API的主要区别在于Window API期望任何矩形都在窗口的坐标空间中。如果使用视图,通常将改为在视图坐标空间中工作。View API负责坐标空间之间的转换,也就是说,您仅需要根据视图的内容进行推理。让我们来看一个例子。我们将再次使用音乐播放器的示例,该示例SeekBar
位于屏幕的整个宽度上。SeekBar
在上一节中,我们用返回主屏幕的手势解决了冲突,但是仍然需要注意手势的左右两个区域。让我们看看当“滑块”用户尝试跳过歌曲时会发生什么SeekBar
(圆形拖动)位于边缘之一附近:
SeekBar与后手势区域冲突由于滑块位于右手势区域下方,因此系统认为用户正在尝试使用该手势返回,因此显示了后箭头。对于用户而言,这很不方便,因为他们现在可能不想返回。我们可以使用上述手势排除API来解决此问题,以排除滑块的边框。手势异常API通常在两个地方调用:onDraw()
渲染视图时,onLayout()
否则。您的观点通过了List<
Rect>
包含所有要排除的矩形。如前所述,这些矩形必须位于其自己的视图坐标系中。通常,您会创建一个类似这样的函数,该函数将从onLayout()
和/或调用onDraw()
:private val gestureExclusionRects = mutableListOf<Rect>()
private fun updateGestureExclusion() {
if (Build.VERSION.SDK_INT < 29) return
gestureExclusionRects.clear()
thumb?.also { t ->
gestureExclusionRects += t.copyBounds()
}
systemGestureExclusionRects = gestureExclusionRects
}
一个完整的例子可以在这里找到。添加此内容后,可以按预期方式在边缘附近倒回:
SeekBar在后手势区域中工作请注意上面的示例。SeekBar
已经在Android 10中为您自动执行此操作,因此您无需自己执行此操作。在这里,我们只是作为示例向您展示总体轮廓。局限性
尽管手势排除API似乎是解决所有手势冲突的完美解决方案,但实际上并非如此。使用手势排除API,您可以声明应用程序的手势比系统返回操作更为重要。这是一个强有力的声明,因此,当您无能为力时,此API旨在作为紧急出口。使用手势排除API,您可以声明应用程序的手势比回溯的系统操作更重要。由于API提供的行为可能会违反舒适的用户体验,因此系统限制了它的使用:应用程序每个边缘最多只能排除200dp。这是开发人员听到这些时遇到的一些常见问题:为什么需要限制?我希望上面的解释已经使您有一个原因。我们认为,对于用户而言,始终如一地从侧面滑动回去非常重要。始终贯穿整个设备,而不是一个应用程序。这种限制似乎过于严格,但是仅一个应用程序(不包括屏幕的整个边缘)足以给用户带来不便,这将导致该应用程序的删除或更为彻底的操作。换句话说,导航系统应始终保持一致且易于使用。为什么是200dp?200dp的论点非常简单。正如我们之前提到的,手势排除API旨在作为最后的手段,因此此限制的计算方法是几个重要触摸目标的倍数。感觉目标的最小建议尺寸为48dp
.4感觉目标× 48dp = 192dp
。添加更多的缩进,我们得到了价值200dp
。如果我需要排除每个边缘200dp以上的像素怎么办?系统将仅排除您要求的最低200dp。
从屏幕外“我的视图” 的底部边缘算起,系统允许请求总高度为200 dp,这是否被视为限制?不,系统仅考虑屏幕内排除的矩形。同样,如果视图部分位于屏幕上,则仅考虑所请求矩形的可见部分。深入了解下一篇文章
也许在阅读完这篇文章之后,您想知道为什么我们没有考虑流程图的右侧。以下解决方案专为使用整个屏幕进行渲染的应用程序而设计。我们将在下一部分中讨论它们。