共计 6151 个字符,预计需要花费 16 分钟才能阅读完成。
Android 11 中的新性能之一是能够让利用在对于屏幕上的软键盘关上和敞开的过程创立无缝过渡的动画成果,这一性能源自 Android 11 中对 WindowInsets API 的大量改良。
在 Android 11 上有两个针对该性能的例子——这个性能曾经被集成到 Google Search 利用和 Messages 利用中了:
两个 Android 11 中软键盘动画成果的示例: Google Search 利用 (左),Messages (右)
让咱们来看看如何在您的利用中增加这种用户体验。总共分为三步:
- 首先,咱们须要做到 “ 边到边 ” (edge-to-edge);
- 第二步,利用须要针对边衬区动画做出反馈;
- 最初第三步就是利用在失当的场景中管制并应用边衬区动画。
下面的每一步都环环相扣,所以咱们会在不同的文章中别离介绍。在这个系列的 第一部 中,咱们会介绍如何实现边到边,以及 Android 11 中相干 API 的改变。
实现边到边 (edge-to-edge)
去年咱们介绍了一个对于实现 “ 边到边 ” 的概念,这个办法能够让利用深度利用 Android 10 的手势导航: 开启全面屏体验 | 手势导航 (一)。
简略回顾一下,实现 “ 边到边 ” 会让您的利用渲染在零碎状态栏的前面,如上图所示。
援用去年我本人的话:
实现从边到边的全面屏体验后,零碎栏会笼罩在利用内容后方。利用也得以通过更大幅面的内容为用户带来更具备冲击力的体验。
实现边到边跟软键盘有什么关系?
其实,实现边到边不单单只是在状态栏和导航栏之后渲染。利用自身须要开始负责解决那些跟利用重叠的零碎 UI 的局部。
正如咱们后面提到的,两个最直观的例子是状态栏和导航栏。除此之外还有软键盘,有时候也叫 IME (输入法编辑器),这是另外一个咱们须要理解的零碎 UI。
利用如何实现边到边?
如果咱们回忆 去年的介绍,实现边到边能够分为三步:
- 扭转零碎栏的色彩
- 设置全屏布局
- 解决视觉抵触
咱们会跳过第一步,因为从去年至今这个局部没有改变。教程中的第二步和第三步有一些针对 Android 11 的改变,让咱们来看一下。
2: 设置全屏布局
在以往的第二步中,利用须要应用 systemUiVisibility) API 以及一些参数来设置全屏布局:
view.systemUiVisibility =
// 告诉零碎,视窗心愿在极其的状况下该如何布局内容。查看文档来获取更具体的信息。View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
// 告诉零碎,视窗心愿在导航栏被暗藏的状况下如何布局内容。View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
如果您的我的项目设置编译的指标 SDK 版本曾经降级为 30 并且应用这个 API,您会发现这些 API 都曾经被标示为弃用了。
它们曾经被 Window 的一个叫作 setDecorFitsSystemWindows()
的函数代替了:
// 告诉视窗,咱们(利用)会解决任何零碎视窗(而不是 decor)window.setDecorFitsSystemWindows(false)
// 或者您能够应用 AndroidX v1.5.0-alpha02 中的 WindowCompat
WindowCompat.setDecorFitsSystemWindows(window, false)
取代那些参数的是一个布尔值 false,它的意思是利用会解决任何零碎窗口的适配 (换句话说就是全屏)。
在 WindowCompat 中,咱们还有一个 Jetpack 版本的该函数,androidx.core 库的 v1.5.0-alpha02 版本里也蕴含了这个函数。
以上就是第二步的改变。
3: 解决视觉抵触
当初让咱们来看一下第三步: 防止与零碎 UI 产生重叠,也能够说是应用视窗边衬区来决定如何挪动利用的内容来防止与零碎 UI 的抵触。在 Android 零碎中,边衬区能够通过 WindowInsets 类和 AndroidX 中的 WindowInsetsCompat 来拜访。
如果咱们查看 API 30 以前版本的 WindowInsets,最罕用的边衬区类型是零碎视窗边衬区。这些边衬区包含了状态栏、导航栏以及关上时的软键盘。
为了应用 WindowInsets,您通常须要在一个视图上增加 OnApplyWindowInsetsListener,并且在这个函数中解决传进来的边衬区:
ViewCompat.setOnApplyWindowInsetsListener(view) { v, insets ->
v.updatePadding(bottom = insets.systemWindowInsets.bottom)
// 返回边衬区,这样它们才可能持续在视图树中持续传递上来
insets
}
在这个例子中,咱们获取到 零碎视窗边衬区,而后更新视图的内边距,这是一个常见的利用场景。
还有一些其余类型的边衬区,比方 Android 10 最近新增的手势边衬区:
ViewCompat.setOnApplyWindowInsetsListener(v) { view, windowInsets ->
val sysWindow = windowInsets.systemWindowInsets
val stable = windowInsets.stableInsets
val systemGestures = windowInsets.systemGestureInsets
val tappableElement = windowInsets.tappableElementInsets
}
和 systemUiVisibility API) 相似,许多 WindowInsets API 曾经被弃用了,取而代之的一些新函数来查问不同类型的边衬区:
- getInsets(type: Int)) 会返回指定类型的可见边衬区。
- getInsetsIgnoringVisibility(type: Int)) 会返回所有边衬区,无论它们是否可见。
- isVisible(type: Int)) 会返回 true 如果指定的类型是可见的。
咱们刚刚屡次提到 “ 类型 ”,它们在 WindowInsets.Type 类中被定义为函数,每个函数都会返回一个整数标示。咱们稍后还会展现如何应用 OR 位运算来查问联合到一起的类型。
所有这些 API 都曾经被增加到 AndroidX Core 中的 WindowInsetsCompat,并且向前兼容到 API 14 (请查看 发行注记 来获取更多信息)。
再来看如果咱们用新的 API 来更新之前的示例,它们就变成:
ViewCompat.setOnApplyWindowInsetsListener(...) { view, insets ->
- val sysWindow = insets.systemWindowInsets
+ val sysWindow = insets.getInsets(Type.systemBars() or Type.ime())
- val stable = insets.stableInsets
+ val stable = insets.getInsetsIgnoringVisibility(Type.systemBars())
- val systemGestures = insets.systemGestureInsets
+ val systemGestures = insets.getInsets(Type.systemGestures())
- val tappableElement = insets.tappableElementInsets
+ val tappableElement = insets.getInsets(Type.tappableElement())
}
软键盘类型 ⌨️
这会儿那些敏锐的 ???? 可能曾经开始盯着这个类型列表,尤其是其中的 软键盘类型)。
在捷足先登了十年后,咱们终于能够答复这个对于如何查看软键盘可见性的 StackOverflow 问题。????
- 在 Android 中如何查看软键盘的可见性?
为了获取以后软键盘的可见性,咱们能够获得根视窗的边衬区,而后执行 isVisible() 函数并传入 IME) 类型。
同样地,如果咱们想查出高度,咱们也能够通过雷同的办法实现:
val insets = ViewCompat.getRootWindowInsets(view)
val imeVisible = insets.isVisible(Type.ime())
val imeHeight = insets.getInsets(Type.ime()).bottom
如果咱们须要监听软键盘的扭转,咱们能够照常应用 OnApplyWindowInsetsListener,并且应用同样的函数:
ViewCompat.setOnApplyWindowInsetsListener(view) { v, insets ->
val imeVisible = insets.isVisible(Type.ime())
val imeHeight = insets.getInsets(Type.ime()).bottom
}
暗藏或显示软键盘
既然咱们正在答复 StackOverflow 上的问题,来看一下这个 11 年前对于如何敞开软键盘的问题。
- 如何敞开 / 暗藏 Android 软键盘?
这一次咱们要介绍 Android 11 的一个新 API,它叫 WindowInsetsController。
利用能够从任何视图取得一个控制器,而后咱们就能够通过传入 IME 类型,并执行 show()) 或者 hide()) 函数来实现显示或暗藏软键盘:
val controller = view.windowInsetsController
// 显示软键盘(IME)controller.show(Type.ime())
// 暗藏软键盘
controller.hide(Type.ime())
然而,这个控制器不单单能管制暗藏和显示软键盘 …
WindowInsetsController
之前咱们提到过,有一些 View.SYSTEM_UI_*
标记曾经在 Android 11 中被弃用,并且被新的 API 代替。还有一些 View.SYSTEM_UI
标记原本是被用来扭转零碎 UI 的外观和可见性的,包含:
- View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
- View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
- View.SYSTEM_UI_FLAG_LAYOUT_STABLE
- View.SYSTEM_UI_FLAG_LOW_PROFILE
- View.SYSTEM_UI_FLAG_FULLSCREEN
- View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
- View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
- View.SYSTEM_UI_FLAG_IMMERSIVE
- View.SYSTEM_UI_FLAG_VISIBLE
- View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
- View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR
和之前的标记相似,这些也都在 API 30 中被弃用,并被 WindowInsetsController 中的 API 代替。
接下来咱们会通过几个常见的利用场景来介绍如何更新这些标记,而不是一一介绍所有这些标记的扭转:
沉迷模式
如图所示,这个绘图利用暗藏了零碎 UI 来让绘图区域最大化:
Markers 利用,展现暗藏零碎 UI
为了实现这个成果,咱们像以前一样应用 WindowInsetsController 来执行 hide()) 和 show()) 函数,然而这一次咱们要传入零碎栏类型:
val controller = view.windowInsetsController
// 当咱们想暗藏零碎栏
controller.hide(Type.systemBars())
// 当咱们想显示零碎栏
controller.show(Type.systemBars())
利用应用 沉迷模式 来让用户在零碎栏暗藏的时候能够通过滑动来召回零碎栏。为了实现这个成果,咱们应用 WindowInsetsController 并且扭转 setSystemBarsBehavior()) 为 BEHAVIOR_SHOW_BARS_BY_SWIPE:
val controller = view.windowInsetsController
// 当初开始沉迷式..
controller.setSystemBarsBehavior(WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE)
// 当咱们想要暗藏零碎栏
controller.hide(Type.systemBars())
相似地,如果您之前应用吸附式的 沉迷模式,这个当初也能够用 BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE 来实现:
val controller = view.windowInsetsController
// 当初开始吸附式沉迷式体验 ...
controller.setSystemBarsBehavior(BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE)
// 当咱们想要暗藏零碎栏
controller.hide(Type.systemBars())
状态栏内容的色彩
接下来的这个利用场景是围绕着状态栏内容的色彩。您会看到如下两个利用:
两个利用,右边的应用的是深色状态栏背景,左边的应用的是浅色背景
右边的利用应用的是一个深色的状态栏背景,而它的内容用的是浅色,比方工夫和图标。可如果咱们想实现一个浅色的状态栏背景并且搭配深色的内容,像左边显示的一样,咱们也能够应用 WindowInsetsController。
要实现这个成果,咱们能够应用 setSystemBarsAppearance()) 函数,传入 APPEARANCE_LIGHT_STATUS_BARS 值:
val controller = view.windowInsetsController
// 启用浅色状态栏内容
controller.setSystemBarsAppearance(
APPEARANCE_LIGHT_STATUS_BARS, // value
APPEARANCE_LIGHT_STATUS_BARS // mask
)
但如果您想设置一个深色的状态栏,能够传入 0,而不是革除那个值。
留神: 您也能够在主题中通过设置
android:windowLightStatusBar
实现上述成果。在您晓得这个值不会变动的状况下,这个形式可能更好。
APPEARANCE_LIGHT_NAVIGATION_BARS 标记能够给导航栏提供相似的性能。
AndroidX 中的 WindowInsetsController?
惋惜的是这个 API 的 Jetpack 版本还没有上线,而咱们正在加紧筹备,敬请关注。
实现边到边: ✔️
咱们的第一步实现了。在本系列下一篇文章中,咱们会钻研第二步: 利用对于边衬区的响应式动画。敬请关注。