Hybrid Android app 键盘遮挡问题

闲言

因为之前我们开发时是直接在 chrome 上调试的,在设备上没有经过全面的测试,所以遗留了一堆问题,为了把这个键盘遮挡问题刨根究底真是花了九牛二虎之力啊,不仅涉及 web,还涉及原生 Android,要处理这个问题,首先需要弄清楚几个概念。咱直接进入正题吧。

Android 之全屏与非全屏


全屏模式:隐藏掉上述所显示的导航栏、状态栏(除沉浸式)后,只剩下 WebView。
非全屏模式:除 WebView 之外,存在占用空间的 Android 组件如状态栏、导航栏等。

软键盘弹出分析

我们现在的 app 在原生的处理如下:

1.非全屏下 android:windowSoftInputMode 设置的是 adjustResize。即当软键盘弹出时,会让布局重新绘制,这种一般适应于带有滑动性质的控制,让其向下滚动,然后适应软键盘的显示。

2.全屏下 使用 AndroidBug5497Workaround 这个类在软键盘弹出时,获取改变之后的界面的可用高度(可以被开发者用以显示内容的高度),此时当前界面的实际高度是比可用高度要多出一个软键盘的距离的。
因此最后将界面高度置为可用高度。效果基本与设置了 adjustResize 相当。

为啥会有两种情况呢,这坑就说来话长了,大家可以参考:
Android 爬坑之旅:软键盘挡住输入框问题的终极解决方案
如果有兴趣了解 Android 软键盘的朋友,可以参考:
Android 软键盘的全面解析
原生系统该做的都做了,那么剩下的坑(如果还有的话)就应该交给我们前端工程师了在 Web 页面这做处理了。

Web处理

由上边的分析可知,原生的处理方式都是以压缩 WebView 占据的空间来显示键盘的。如图:

我这 app 的 html 的 width、height 均设置为 100%
键盘弹出前:

键盘弹出后:

那么在 Web 这边我们就不必要区分全面屏还是非全面屏了,直接根据键盘原理及 web 出现的现象直接分析就是了,给大家看看我遇到的情况及解决办法吧。

情况一

input在获取焦点的时候并不会触发页面上滑,只有在输入第一个字符的时候才能触发,若是页面偏下边的输入框,则会出现键盘遮挡的问题。
点击前:

点击后:

分析:html 是随同 WebView 等比例缩小了,你要触发滑动,肯定不能让 html 缩小,所以我们要保持它本来的大小。

解决办法:在页面刚打开的时候便获取 html 宽高,然后通过 style 的方式赋值回 html,这样键盘弹出时无法改变 html 的高度。

// App.vue
mounted() {
    document.documentElement.style.height = document.documentElement.clientHeight + 'px';
    document.documentElement.style.width = document.documentElement.clientWidth + 'px';
}

处理后效果:

注:往后的几种情况都是是基于给 html 固定宽高这个前提进行的

情况二

使用了 fixed 来布局,导致盒子随着视窗一块被压缩
点击前:

点击后:

分析:我去查了一下这块布局的代码,发现包裹它的 div 的样式是 fixed,相对视窗固定,那么它肯定会随着视窗一块被压缩宽高。

解决办法:不用 fixed 定位,使用 flex 布局,让他们保持居中。

// 父元素样式
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;

处理后效果:

情况三

不得不使用 fixed,如 POP 窗。
点击前:

点击后:

分析:这块的布局是这样,信息填写是一部分,按钮是 absolute 定位的。整个 POP 窗是 fixed 定位的,宽高设置的都是百分比。
解决办法:给这个 POP 窗固定宽高,按钮不用定位,重新调整样式。
处理后效果:

大家肯定在想,我这是刚好 input 框靠上,如果靠下的话,肯定不好使。你猜对了,我是特意去试了一下,效果:
点击前:

点击后:

对于 input 框一定要放下边的这种情况,我暂时也没有找到最优的办法。我个人想法是:监听resize事件,然后计算偏移量,将 POP 滚动距离设置为偏移量大小。