解决安卓收起键盘无法触发失焦事件的问题

简介: 解决安卓收起键盘无法触发失焦事件的问题

正文


一、背景


最近在做一个移动端 Web 项目,在首页底部是有一个类似于 APP 导航栏(以下称 FootNav),采用的 fixed 布局固定于底部。同时页面有一些 <input> 输入框(以下称 Input)。

当聚焦于 Input 时,在 iOS 预期效果是没问题,但是在杀千刀的 Android 上,页面高度发生变化,导致 FootNav 固定在手机键盘上面,同时 FootNav 也直接挡住了输入框,交互体验非常的糟糕。

烦死了...


二、iOS 与 Android 键盘弹出收起的表现


先了解下背景,键盘的弹出收起,在 iOS 端与 Android 端的 WebView 中表现并非一致的。


1. 键盘弹出
  • iOS
    在 iOS 系统的键盘处于窗口的最上层。当键盘弹出时,WebView 的高度 height 并没有发生改变,只是 scrollTop 发生改变。页面可以滚动,且页面可滚动的最大限度为弹出键盘的高度,而只有键盘弹出时页面恰好也滚动到最底部时,scrollTop 的变化值为键盘高度。
  • Android
    在 Android 系统中,键盘也是处于窗口的最上层。键盘弹出时,页面高度发生变化,如果输入框在靠近底部的话,就会被键盘挡住,只有你输入的时候才会滚动到可视化区域。


2. 键盘收起


  • iOS
    当触发键盘上按钮收起键盘或者输入框以外的区域时,输入框会失去焦点,因此会触发输入框的 blur 失焦事件。
  • Android
    当触发键盘上的按钮收起键盘时,输入框并不会失去焦点,因此不会触发输入框的 blur 事件;触发输入框以外的区域时,输入框会失去焦点,触发输入框的 blur 事件。

由于我并没有过多的深入了解两者的差异表现,以上内容来自此处


三、寻找解决方案


针对 Android 设备做处理就行了,iOS 无需处理。


1. 方案一


处理方式:Input 聚焦隐藏 FootNav,失焦时再将其显示出来。(同理,修改布局方式也是一样的)


首先这种处理思路是没毛病的,但是...


某些机型、某些输入法,收起键盘并不会触发输入框的 blur 失焦事件,导致该方案直接流产。


2. 方案二

监听页面高度的变化,利用这一点我们就可以处理 FootNav 的隐藏/显示了。


四、实现


思路很简单:首先进入页面时,先记录窗口的原始高度。每当 Input 聚焦时,设置 window.onresize 函数,当窗口宽高发生改变时便会触发。


以 React 为例:

class Home extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      showFootNav: true,
      initClientHeight: document.documentElement.clientHeight // 记录初始高度
    }
  }
  // 判断 Android 设备
  isAndroid() {
    const ua = navigator.userAgent.toLowerCase()
    return ua.includes('android') || ua.includes('linux')
  }
  // 监听窗口变化
  listenWindowResize() {
    let that = this
    if (this.isAndroid()) {
      window.onresize = () => {
        const { initClientHeight } = that.state
        const curClientHeight = document.documentElement.clientHeight // 当前页面高度
        if (curClientHeight < initClientHeight) {
          // 键盘弹出
          that.setState({ showFootNav: false })
        } else {
          // 键盘收起
          that.setState({ showFootNav: true }, () => {
            window.onresize = null // 清空 onresize
          })
        }
      }
    }
  }
  render() {
    const { showFootNav } = this.state
    // 以下 Input、FootNav 为自定义封装的组件
    return (
      <div>
        <Input onFocus={this.listenWindowResize.bind(this)} />
        {showFootNav ? <FootNav /> : null}
      </div>
    )
  }
}


本文之外的一些兼容性问题,iOS 也有一些 bug 的,比如微信浏览器里收起键盘之后,页面不回弹,可参考文章


References



The end.


目录
相关文章
|
2月前
|
Android开发
Android面试高频知识点(1) 图解Android事件分发机制
Android面试高频知识点(1) 图解Android事件分发机制
|
2月前
|
Android开发
Android面试高频知识点(1) 图解 Android 事件分发机制
Android面试高频知识点(1) 图解 Android 事件分发机制
45 1
|
2月前
|
XML 前端开发 Android开发
Android面试高频知识点(1) 图解Android事件分发机制
Android面试高频知识点(1) 图解Android事件分发机制
Android面试高频知识点(1) 图解Android事件分发机制
|
2月前
|
Android开发
Android 事件分发机制详细解读
Android 事件分发机制详细解读
44 5
|
3月前
|
Android开发
Android学习 —— 测试init.rc中的条件触发的处理顺序
Android学习 —— 测试init.rc中的条件触发的处理顺序
|
4月前
|
图形学 Android开发
小功能⭐️Unity调用Android常用事件
小功能⭐️Unity调用Android常用事件
|
4月前
|
Android开发
Android面试高频知识点(1) 图解 Android 事件分发机制
在Android开发中,事件分发机制是一块Android比较重要的知识体系,了解并熟悉整套的分发机制有助于更好的分析各种点击滑动失效问题,更好去扩展控件的事件功能和开发自定义控件,同时事件分发机制也是Android面试必问考点之一,如果你能把下面的一些事件分发图当场画出来肯定加分不少。废话不多说,总结一句:事件分发机制很重要。
202 9
|
4月前
|
开发工具 Android开发
Android项目架构设计问题之组件A通知组件B某个事件的发生如何解决
Android项目架构设计问题之组件A通知组件B某个事件的发生如何解决
43 0
|
5月前
|
存储 API Android开发
kotlin开发安卓app,使用webivew 触发 onShowFileChooser, 但只能触发一次,第二次无法触发,是怎么回事。 如何解决
在Android WebView开发中,`onShowFileChooser`方法用于开启文件选择。当用户只能选择一次文件可能是因为未正确处理选择回调。解决此问题需确保:1) 实现`WebChromeClient`并覆写`onShowFileChooser`;2) 用户选择文件后调用`ValueCallback.onReceiveValue`传递URI;3) 传递结果后将`ValueCallback`设为`null`以允许再次选择。下面是一个Kotlin示例,展示如何处理文件选择和结果回调。别忘了在Android 6.0+动态请求存储权限,以及在Android 10+处理分区存储。
|
5月前
|
测试技术 Android开发
Android中使用performClick触发点击事件
Android中使用performClick触发点击事件