Android 11 中的新功能之一是可以让应用在对于屏幕上的软键盘打开和关闭的过程创建无缝过渡的动画效果,这一功能源自 Android 11 中对 WindowInsets API 的大量改进。在 Android 11 上有两个针对该功能的例子——这个功能已经被集成到 Google Search 应用和 Messages 应用中了:

两个 Android 11 中软键盘动画效果的示例: Google Search 应用 (左),Messages (右)

  • Android 11https://developer.android.google.cn/android11
  • WindowInsets https://developer.android.google.cn/reference/kotlin/android/view/WindowInsets
  • Google Searchhttps://play.google.com/store/apps/details?id=com.google.android.googlequicksearchbox&hl=en_GB
  • Messageshttps://play.google.com/store/apps/details?id=com.google.android.apps.messaging

让我们来看看如何在您的应用中添加这种用户体验。总共分为三步:

  • 首先,我们需要做到 "边到边" (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
  • systemUiVisibilityhttps://developer.android.google.cn/reference/android/view/View.html#setSystemUiVisibility(int)

如果您的项目设置编译的目标 SDK 版本已经升级为 30 并且使用这个 API ,您会发现这些 API 都已经被标示为弃用了。它们已经被 Window 的一个叫作 setDecorFitsSystemWindows() 的函数替代了:

// 通知视窗,我们(应用)会处理任何系统视窗(而不是 decor)window.setDecorFitsSystemWindows(false)// 或者您可以使用 AndroidX v1.5.0-alpha02 中的 WindowCompatWindowCompat.setDecorFitsSystemWindows(window, false)

取代那些参数的是一个布尔值 false,它的意思是应用会处理任何系统窗口的适配 (换句话说就是全屏)。在 WindowCompat 中,我们还有一个 Jetpack 版本的该函数,androidx.core 库的 v1.5.0-alpha02 版本里也包含了这个函数。以上就是第二步的改动。

  • WindowCompathttps://developer.android.google.cn/reference/kotlin/androidx/core/view/WindowCompat

#3: 处理视觉冲突

现在让我们来看一下第三步: 避免与系统 UI 产生重叠,也可以说是使用视窗边衬区来决定如何移动应用的内容来避免与系统 UI 的冲突。在 Android 系统中,边衬区可以通过 WindowInsets类和 AndroidX 中的 WindowInsetsCompat来访问。如果我们查看 API 30 以前版本的 WindowInsets,最常用的边衬区类型是系统视窗边衬区。这些边衬区包括了状态栏、导航栏以及打开时的软键盘。为了使用 WindowInsets,您通常需要在一个视图上添加 OnApplyWindowInsetsListener,并且在这个函数中处理传进来的边衬区:

ViewCompat.setOnApplyWindowInsetsListener(view) { v, insets ->    v.updatePadding(bottom = insets.systemWindowInsets.bottom)    // 返回边衬区,这样它们才能够继续在视图树中继续传递下去    insets}

在这个例子中,我们获取到系统视窗边衬区,然后更新视图的内边距,这是一个常见的应用场景。

  • WindowInsets

    https://developer.android.google.cn/reference/kotlin/android/view/WindowInsets

  • WindowInsetsCompat

    https://developer.android.google.cn/reference/kotlin/androidx/core/view/WindowInsetsCompat

  • OnApplyWindowInsetsListener

    https://developer.android.google.cn/reference/androidx/core/view/OnApplyWindowInsetsListener

  • 系统视窗边衬区

    https://developer.android.google.cn/reference/kotlin/androidx/core/view/WindowInsetsCompat#getsystemwindowinsets

还有一些其他类型的边衬区,比如 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 (请查看发行注记来获取更多信息)。

  • systemUiVisibility APIhttps://developer.android.google.cn/reference/android/view/View.html#setSystemUiVisibility(int)
  • getInsets(type: Int)https://developer.android.google.cn/reference/android/view/WindowInsets#getInsets(int)
  • getInsetsIgnoringVisibility(type: Int)https://developer.android.google.cn/reference/android/view/WindowInsets#getInsetsIgnoringVisibility(int)
  • isVisible(type: Int)https://developer.android.google.cn/reference/android/view/WindowInsets#isVisible(int)
  • WindowInsets.Typehttps://developer.android.google.cn/reference/kotlin/android/view/WindowInsets.Type
  • AndroidX Corehttps://developer.android.google.cn/jetpack/androidx/releases/core
  • 发行注记https://developer.android.google.cn/jetpack/androidx/releases/core#1.5.0-alpha02

再来看如果我们用新的 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 中如何查看软键盘的可见性?

    https://stackoverflow.com/questions/2150078/how-to-check-visibility-of-software-keyboard-in-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}
  • 软键盘类型https://developer.android.google.cn/reference/android/view/WindowInsets.Type#ime()
  • isVisible()https://developer.android.google.cn/reference/kotlin/android/view/WindowInsets#isvisible
  • IMEhttps://developer.android.google.cn/reference/kotlin/android/view/WindowInsets.Type#ime()
  • OnApplyWindowInsetsListenerhttps://developer.android.google.cn/reference/androidx/core/view/OnApplyWindowInsetsListener

隐藏或显示软键盘

既然我们正在回答 StackOverflow 上的问题,来看一下这个 11 年前关于如何关闭软键盘的问题。

  • 如何关闭/隐藏 Android 软键盘?https://stackoverflow.com/questions/1109022/how-do-you-close-hide-the-android-soft-keyboard-using-java

这一次我们要介绍 Android 11 的一个新 API,它叫 WindowInsetsController。应用可以从任何视图获得一个控制器,然后我们就可以通过传入 IME 类型,并执行 show() 或者 hide() 函数来实现显示或隐藏软键盘:

val controller = view.windowInsetsController// 显示软键盘( IME )controller.show(Type.ime())// 隐藏软键盘controller.hide(Type.ime())

然而,这个控制器不单单能控制隐藏和显示软键盘...

  • WindowInsetsController https://developer.android.google.cn/reference/android/view/WindowInsetsController
  • show()https://developer.android.google.cn/reference/android/view/WindowInsetsController#show(int)
  • hide()https://developer.android.google.cn/reference/android/view/WindowInsetsController#hide(int)

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 代替。接下来我们会通过几个常见的应用场景来介绍如何更新这些标志,而不是一一介绍所有这些标志的改变:

  • WindowInsetsControllerhttps://developer.android.google.cn/reference/android/view/WindowInsetsController

沉浸模式

如图所示,这个绘图应用隐藏了系统 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())
  • WindowInsetsControllerhttps://developer.android.google.cn/reference/android/view/WindowInsetsController
  • 沉浸模式https://developer.android.google.cn/training/system-ui/immersive#immersive
  • setSystemBarsBehavior()https://developer.android.google.cn/reference/android/view/WindowInsetsController#setSystemBarsBehavior(int)
  • BEHAVIOR_SHOW_BARS_BY_SWIPEhttps://developer.android.google.cn/reference/android/view/WindowInsetsController#BEHAVIOR_SHOW_BARS_BY_SWIPE

类似地,如果您之前使用吸附式的沉浸模式,这个现在也可以用 BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE来实现:

val controller = view.windowInsetsController// 现在开始吸附式沉浸式体验 ...controller.setSystemBarsBehavior(    BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE)// 当我们想要隐藏系统栏controller.hide(Type.systemBars())
  • BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPEhttps://developer.android.google.cn/reference/android/view/WindowInsetsController#BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE

状态栏内容的颜色

接下来的这个应用场景是围绕着状态栏内容的颜色。您会看到如下两个应用:

两个应用,左边的使用的是深色状态栏背景,右边的使用的是浅色背景

左边的应用使用的是一个深色的状态栏背景,而它的内容用的是浅色,比如时间和图标。可如果我们想实现一个浅色的状态栏背景并且搭配深色的内容,像右边显示的一样,我们也可以使用 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标志可以给导航栏提供类似的功能。

  • setSystemBarsAppearance()

    https://developer.android.google.cn/reference/android/view/WindowInsetsController#setSystemBarsAppearance(int,%20int)

  • APPEARANCE_LIGHT_STATUS_BARS

    https://developer.android.google.cn/reference/android/view/WindowInsetsController#APPEARANCE_LIGHT_STATUS_BARS

  • APPEARANCE_LIGHT_NAVIGATION_BARS

    https://developer.android.google.cn/reference/android/view/WindowInsetsController#APPEARANCE_LIGHT_NAVIGATION_BARS

AndroidX 中的 WindowInsetsController?

可惜的是这个 API 的 Jetpack 版本还没有上线,而我们正在加紧准备,敬请关注。

实现边到边: ✔️

我们的第一步完成了。在本系列下一篇文章中,我们会研究第二步: 应用对于边衬区的响应式动画。敬请关注。


推荐阅读

 点击屏末  | 进一步了解 WindowInsets


android 关闭软键盘_实现边到边的体验 | 让您的软键盘动起来 (一)相关推荐

  1. win10禁用触摸键盘_如何在Windows 8和10中永久禁用触摸键盘

    win10禁用触摸键盘 You may have noticed that, mysteriously, Windows' Touch Keyboard icon keeps appearing in ...

  2. 安卓虚拟pc悬浮键盘_文章荣耀V20云电脑体验:玩PC游戏

    近期荣耀V20云电脑的话题成为科技领域的热点,很多人以为"云电脑"就是此前荣耀"无线投屏",也有人称后者为小屏变大屏.事实并非如此,云电脑与无线投屏是截然不同的 ...

  3. Android --- 进入页面时,不弹出软键盘。当 EditText 被点击时不弹出软键盘,而是调用其他选择器,比如时间选择器等

    功能需求: 1.进入页面时,不弹出软键盘 2.当 EditText 被点击时不弹出软键盘,而是调用其他选择器,比如时间选择器等 搜索百度测试: 1.百度上说用 editText.setInputTyp ...

  4. android自定义金额输入键盘_触摸键盘的设计解析

    本文为PMCAFF作者 呆呆丶于社区发布 众所周知,移动设备的触摸键盘相比于电脑的实体键盘而言,易用性较差.原因有很多,比如使用习惯,人机工程原理,触摸实感差异等等.这些原因导致我们在使用移动设备的触 ...

  5. java怎么编写软键盘_输入法编程教程---软键盘(小键盘?)类,C++编写

    //这是以前写的输入法的小键盘(软键盘)类 //就是输入法输入特殊字符时用的那个小键盘 //比如搜狗输入法上的小键盘按钮按下之后,就会弹出一个小键盘 // //注意:这是多个文件合在一起了,下载打包好 ...

  6. Android期末复习篇_传智课后习题以及答案(选择、填空、判断、简答、编码题)

    写在前面 此为移动应用开发传智课后习题及答案,供期末复习使用,注意 答案仅供参考 加粗题目为强调 题型涉及:选择.判断.填空.简答.编程题 另8章习题及答案汇总:Android期末复习篇_8章节练习题 ...

  7. 2009.5.23软考_系统分析师 上午试题 答案 讨论

    2009.5.23软考_系统分析师 上午试题 答案 讨论 不求尽如人意,但求无愧我心.不断更新中,敬请持续关注--

  8. 2009.5.23软考_信息处理技术员 上午试题 答案 讨论

    2009.5.23软考_信息处理技术员 上午试题 答案 讨论 不求尽如人意,但求无愧我心.不断更新中,敬请持续关注-- 转载于:https://blog.51cto.com/296525818/160 ...

  9. linux 服务器鼠标右键失灵_【华鹏客服维修部】系统重置后,键盘失灵怎么办?...

    哈喽,大家好 使用Windows 10系统重置功能后会出现键盘失灵,用户名无法输入的问题今天小微就来可以大家分享一下出现此类问题的临时解决方法一起来看看吧~温馨提示: 本文中的工具仅适用于重置后所有按 ...

最新文章

  1. 我用 YOLOv5 做情感识别!
  2. 电影中成千上万的群众演员是怎么来的?
  3. 华为配置SSH登陆详细步骤
  4. bat面试题 python 单链表反转排序
  5. 转载 二叉树的创建、遍历、深度、叶子节点数
  6. 【房价地图】以城市为单位,2019
  7. mysql insert 运算_MySql insert插入操作的3个小技巧分享
  8. 【渝粤教育】电大中专市场营销管理 (2)_1作业 题库
  9. aba问题mysql_Mysql中select + update并发更新问题
  10. 分页,在第一页不显示上一页或禁止使用上一页
  11. 用fft对信号进行频谱分析实验报告_【鼎阳硬件智库原创︱测试测量 】基于全数字中频技术频谱分析仪的工作原理...
  12. linux下chm文件制作,自己动手将在线文档制作成CHM文件
  13. 企业物流管理与最新IT技术
  14. 学习记录:python快递价格计算器练习7.12
  15. stc15流水灯c语言,STC89C52单片机流水灯
  16. linux读取ads1115ADC例程
  17. 【温故而知新】计算机408之计算机网络速记
  18. 复杂业务下,我们为何选择Akka作为异步通信框架?
  19. 微信小程序获取手机号登录流程
  20. nodejs实现VSCode中的open with five server功能

热门文章

  1. IOS-简单WebView的使用
  2. zz 聊聊并发(二)
  3. winCE DEBUGZONE
  4. 爱心的浪漫永不过时—程序猿的小浪漫
  5. ga算法matlab,matlab遗传算法ga函数
  6. cannot find mysql.frm_mysql 问题 - Run佳的个人空间 - OSCHINA - 中文开源技术交流社区...
  7. 墨天轮“我的DBA之路”有奖征文开始啦
  8. 快速掌握:大型分布式系统中的缓存架构
  9. 【全干货】5分钟带你看懂 Docker!
  10. 遇见未来系列专访 | 聆听时代最前沿的声音