从 Android 10(API 级别 29)开始,Android 系统支持完全基于手势的导航。为确保应用与此功能兼容,应用开发者应完成以下两项任务:
1.将应用内容扩展到屏幕边缘(全面屏)。
2.处理存在冲突的应用手势。
默认情况下,应用内容的绘制范围从顶部状态栏下方开始,延伸至底部导航栏上方。(状态栏和导航栏统称为系统栏)
Android 10 及以上版,大多数设备均保留了三键导航模式 (后退、返回首页、最近使用的应用)。
首先将布局扩展至系统栏后方,需要使用setSystemUiVisibility()API以全屏模式布局,我们关注几个接受值:
view.systemUiVisibility =
//告诉系统,窗口希望将内容放置在最极端的情况下
View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
//告诉系统,窗口希望将内容布局为隐藏的导航栏
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
setSystemUiVisibility()具体参考:
setSystemUivisibility详解
Andorid开发者文档
设置完毕后可以看到应用的内容出现在导航栏的后面:
Android10中,只需要将系统栏的颜色设置为透明即可:
<style name="Theme.FullScreen">
<item name="android:navigationBarColor">
@android:color/transparent
</item>
<item name="android:statusBarColor">
@android:color/transparent
</item>
</style>
但是在Android9及以下还没有支持手势导航,需要把系统栏设置为半透明,以确保内容可以看见
<style name="Theme.FullScreen">
<item name="android:navigationBarColor">
#B3FFFFFF
</item>
</style>
<!-- values-night/themes.xml -->
<style name="Theme.MyApp">
<item name="android:navigationBarColor">
#B3000000
</item>
</style>
如果应用为自定义视图层次结果,需要手动占用系统窗口边衬区,可以通过实现OnApplyWindowInsetsListener 接口做到这一点:
view.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
@Override
public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
// 1. 通过 insets.getsystemwindowinsettop ()将视图从上到下移动
// 2. 通过 insets.getsystemwindowinsetbottom ()在底部边缘上移动视图
// 3.还要检查 getsystemwindowinsetleft/right () ,例如 landscape
return insets.consumeSystemWindowInsets();
}
});
例如,给某一个组件增加边距,使其不被系统栏覆盖(要确保接口计算要有幂等性):
ViewCompat.setOnApplyWindowInsetsListener(view) {
v, insets ->
v.updatePadding(bottom = insets.systemWindowInsets.bottom)
// Return the insets so that they keep going down the view hierarchy
insets
}
手势冲突就是手势导航与开发者以前设计的一些组件重叠,比如SeekBar在边缘会与返回手势冲突,边缘拖动SeekBar会被认为是在执行返回操作。
新的返回系统手势是从屏幕左侧或右侧边缘向内滑动。这可能会干扰这些区域中的应用导航元素。如需保持位于屏幕左侧和右侧边缘的元素的功能,我们需要告知系统哪些区域需要接收轻触输入,从而选择性地停用返回手势。为此,我们可以将 List 传递给 Android 10 中引入的 View.setSystemGestureExclusionRects() API。从 androidx.core:core:1.1.0-dev01 开始,ViewCompat 中也提供这种方法。
例如:
List<Rect> exclusionRects;
public void onLayout(
boolean changedCanvas, int left, int top, int right, int bottom) {
//更新矩形边界和独占列表
setSystemGestureExclusionRects(exclusionRects);
}
public void onDraw(Canvas canvas) {
//更新矩形边界和独占列表
setSystemGestureExclusionRects(exclusionRects);
}
当视图被布局时 (onLayout),或是当视图被绘制时 (onDraw)就切出 (即不响应系统手势) 的矩形区域。(需要在Android10+上运行 29以上)
由于这个 API 会一定程度上破坏用户习惯的操作,因此系统做出了限制: 屏幕的每个边缘最多只能被应用切除 200dp
新的主屏幕/快速切换系统手势都涉及在屏幕底部导航栏以前占用的空间内滑动。应用无法像停用返回手势一样停用这些手势(强制交互区域)。
为了缓解这个问题,Android 10 引入了 WindowInsets.getMandatorySystemGestureInsets() API(getMandatorySystemGestureInsets只包含强制性系统手势区域,是系统手势区域的子集),它会告知应用触摸识别阈值。
获取之后可以将控件移出交互区域
游戏和其他不含视图层次结构的应用通常要求用户在系统手势区域附近执行滑动操作。在这些情况下,游戏可以使用 Window.setSystemGestureExclusionRects() 排除与系统手势预留区域重叠的区域。游戏应确保仅在必要时(例如在玩游戏过程中)排除这些区域。
如果游戏要求用户在主屏幕手势区域附近滑动,应用可以请求以沉浸模式布局。
沉浸模式布局指的是:手机系统的状态栏和软件内容颜色一致,使浏览页面信息更加舒适,视觉上更好看,或者说是将系统的状态栏、导航栏隐藏(使用不同的UI flag)。
可以参考:Android沉浸式状态实现
沉浸模式会在用户与游戏交互时停用系统手势,但允许用户通过从屏幕底部滑动来重新启用系统手势。
手势的导航需要将应用内容扩展到屏幕边缘并且处理存在冲突的应用手势。冲突包含但不限于返回手势冲突、主屏幕冲突,解决方法可以总结为下图
参考:1.https://blog.csdn.net/QQxiaoqiang1573/article/details/79867127
2.https://developer.android.google.cn/reference/android/view/View.html#setSystemUiVisibility%28int%29
作者:宋晓晖