鸿蒙开发:如何解决软键盘弹出后的间距

程序员一鸣
• 阅读 1

前言

本文基于Api13

鸿蒙开发:如何解决软键盘弹出后的间距

近日在查看github中的issue时,发现了一个问题,说的是当自定义弹窗中有TextInput组件时,触摸焦点弹起软键盘后,组件和软键盘之间有一个间距,看到问题后,“我就在想,自定义弹窗,所有的UI都是传入的,是不是在绘制的时候,这个间距是开发者加的呢?”,本着对问题负责的态度,于是就验证问题出现的原因,最后却发现这是鸿蒙系统的问题

为了验证问题,我传入的自定义UI,没有带有一丝间距,弹起后,发现了开发者所提的问题非虚,和软键盘之间,还真有一个小小的间距。

鸿蒙开发:如何解决软键盘弹出后的间距

看着这个间距,发呆了3秒,彻底怀疑了人生,我明明没设置间距啊,但是这个间距从哪来的呢?是不是软键盘弹起后,本身就会有一个间距?

为了验证问题的真实性,我写了一个无比简单的代码,就一个Column组件包裹着一个TextInput组件,让TextInput组件在最底部,代码如下:

Column() {
     TextInput()
   }
   .width("100%")
   .height("100%")
   .justifyContent(FlexAlign.End)
 }

运行,点击输入框:

鸿蒙开发:如何解决软键盘弹出后的间距

运行之后,看着组件和软键盘之间的间距,陷入了沉思,“给我提问题那个朋友,这个问题,我不背锅”。

是不是TextInput组件有这个问题,换一个其他的输入组件就没这个问题了?于是,我又验证了TextArea,RichEditor,发现问题依然存在,显然和组件是没有关系,和软键盘有关系。

接着便查看了Dom结构,发现这是root根节点自带的一个间距。

打开Dom树结构,可以发现,父节点Column是没有间距的,但是距离软键盘那确实有一个间距。

鸿蒙开发:如何解决软键盘弹出后的间距

点击root节点后,就会发现,这个间距是root所带的。

鸿蒙开发:如何解决软键盘弹出后的间距

这就证明了一个问题,那就是,输入框和软键盘之间的间距是系统自带的, 对比前后的高度,可以得出,这个间距是44px。

其实站在我的角度上去看,我觉得这个间距挺好,起码在UI视觉上看着美观。 当然了不能我以为就以为,还是要考虑到实际的需求开发,毕竟有的场景下,就不需要间距。

有问题,就会有解决问题的办法,经过一系列的研究,其实解决起来也十分的简单,总结了有三种方式,大家可以选择适合的方式。

方式一:设置页面避让模式

当我们不设置虚拟键盘的避让模式时,默认是OFFSET模式,也就是上抬模式,就会出现间距的问题,我们可以改为压缩模式RESIZE。

this.getUIContext().setKeyboardAvoidMode(KeyboardAvoidMode.RESIZE)

设置完避让模式后,我们再次运行看下,组件和软键盘之间的间距已经取消了。

鸿蒙开发:如何解决软键盘弹出后的间距

方法二:设置沉浸式布局

设置沉浸式布局时,布局不会避让状态栏与导航栏,组件可能产生与其重叠的情况,这种情况下需要自己设置距离顶部和底部的距离。

window.getLastWindow(getContext(), (_, win) => {
     win.setWindowLayoutFullScreen(true)
   })

设置完之后,效果和方式一是一样的。

鸿蒙开发:如何解决软键盘弹出后的间距

虽然说解决了间距问题,但是,沉浸式之后,由于不会避让状态栏与导航栏,会出现底部的组件被遮挡的情况,也就是如下图所示:

鸿蒙开发:如何解决软键盘弹出后的间距

这种情况下,如果你想实现软键盘弹出后无间距,软键盘收起后,组件在底部导航栏上面,那么就需要代码上的动态设置,两种方式,一种是监听输入框的输入状态变化,另一种是监听软键盘弹出的状态,根据不同的状态,然后给组件设置距离底部的距离即可。

监听输入框的输入状态

全部代码如下,在aboutToAppear方法里,设置沉浸式,获取底部的导航栏高度,在onEditChange方法中,监听输入状态的变化,动态赋值给底部距离属性。

import { window } from '@kit.ArkUI'

@Entry
@Component
struct Index {
  @State marginBottom: number = 0
  private bottomRectHeight: number = 0 //底部导航栏高度

  aboutToAppear(): void {
    window.getLastWindow(getContext(), (_, win) => {
      win.setWindowLayoutFullScreen(true)
      let type = window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR
      let avoidArea = win.getWindowAvoidArea(type)
      // 获取到导航条区域的高度
      this.bottomRectHeight = px2vp(avoidArea.bottomRect.height)
      this.marginBottom = this.bottomRectHeight //初始化默认距离底部距离
    })
  }

  build() {
    Column() {
      TextInput()
        .margin({ bottom: this.marginBottom })
        .onEditChange((isEditing: boolean) => {
          this.marginBottom = isEditing ? 0 : this.bottomRectHeight
        })
    }
    .width("100%")
    .height("100%")
    .justifyContent(FlexAlign.End)
  }
}

运行之后,可以看到,默认在导航栏上面,软键盘弹起后,也没有任何间距。

鸿蒙开发:如何解决软键盘弹出后的间距

监听软键盘弹出状态

无非就是把输入框的输入状态切换为了软键盘的弹出状态。

// 监听软键盘的隐藏和显示
     win.on('keyboardHeightChange', (height) => {
       this.marginBottom = height > 0 ? 0 : this.bottomRectHeight
     })

按照正常逻辑而言,应该和上面的效果是一样的,但偏偏剑走了弯路,当软键盘弹出后输入框明显被遮挡。

鸿蒙开发:如何解决软键盘弹出后的间距

我真实服了这个老六,无非就是换了一个监听方式,怎么还给了一个惊喜呢,针对这种情况,建议直接使用第一种方式,当然了,如果你仍然要用这种方式,也不是不行,软键盘弹出后,需要加上被遮挡的高度,也就是44px。

// 监听软键盘的隐藏和显示
     win.on('keyboardHeightChange', (height) => {
       this.marginBottom = height > 0 ? px2vp(44) : this.bottomRectHeight
     })

方式三、动态设置位置

所谓的动态设置,就是根据软键盘的高度,动态设置组件的位置,也就是需要获取软键盘的高度,当软键盘弹起时,让组件距离底部的距离正好是软键盘的高度,这种方式和沉浸式布局方式有点类似,但是不需要设置沉浸式。

有一点需要注意,那就是需要配置扩展安全区域的类型为SafeAreaType.KEYBOARD,目的防止组件随着软键盘抬起而置顶。

import { window } from '@kit.ArkUI'

@Entry
@Component
struct Index {
  @State marginBottom: number = 0

  aboutToAppear(): void {
    window.getLastWindow(getContext(), (_, win) => {
      let type = window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR
      let avoidArea = win.getWindowAvoidArea(type)
      // 获取到导航条区域的高度
      let bottomRectHeight = px2vp(avoidArea.bottomRect.height)
      // 监听软键盘的隐藏和显示
      win.on('keyboardHeightChange', (height) => {
        this.marginBottom = height > 0 ? px2vp(height) - bottomRectHeight : 0
      })
    })
  }

  build() {
    Column() {
      TextInput()
        .margin({ bottom: this.marginBottom })
    }
    .width("100%")
    .height("100%")
    .justifyContent(FlexAlign.End)
    .expandSafeArea([SafeAreaType.KEYBOARD])
  }
}

以上的代码运行之后,可以发现和上面的效果是一样的,当然了位置,并不一定通过margin,你也可以通过offset属性,或者padding属性等等。

offset({y:-this.marginBottom})

相关总结

还是那句话,自我感觉,鸿蒙系统对于这个间距的处理,我觉得是正常的,毕竟更加符合视觉美观,如果紧挨着展示,反而觉得不太美观;但话又回来,需求是多变的,人无完人,金无足赤,每个产品都有自己的思想,而作为研发的我们,如果你没有反向修改的勇气,那么只管找解决办法即可。

三种方式,比较推荐方式一,简单便捷,一行代码便可以搞定,当然,另外两种也是实现的办法,在实际的开发中,选择适合的即可。

点赞
收藏
评论区
推荐文章
Stella981 Stella981
3年前
Android RecyclerView使用GridLayoutManager间距设置
使用RecyclerView设置间距,需要重写RecyclerView.ItemDecoration这个类。有如下的效果图需要实现,间距只有中间的格子和底部的格式之间有。Paste\_Image.png实现方法很简单,因为这个效果是每一行有3个格子,只要每行的第一个格式左边间距为0即可以。其他都设置左边距和底部距离。代码如下:publ
少湖说 少湖说
7个月前
鸿蒙Flutter实战:05-使用第三方插件
鸿蒙Flutter实战:使用第三方插件在鸿蒙Flutter开发中,如果涉及到使用原生功能,就要使用插件。使用插件有两种方式,一种是自己编写原生ArkTS代码,在Dart侧调用。另外一种是使用第三方代码。方式一:编号原生ArkTS代码该方案可以使用Platf
少湖说 少湖说
7个月前
鸿蒙Flutter实战:06-使用ArkTs开发Flutter鸿蒙插件
使用ArkTs开发Flutter鸿蒙平台插件本文讲述如何开发一个Flutter鸿蒙插件,如何实现Flutter与鸿蒙的混合开发,以及双端消息通信。Flutter侧,编写MethodChanneldartconstMethodChannelmethodCha
少湖说 少湖说
7个月前
鸿蒙Flutter实战:07-混合开发
鸿蒙Flutter实战:混合开发鸿蒙Flutter混合开发主要有两种形式。1.基于har将fluttermodule打包成har包,在原生鸿蒙项目中,以har包的方式引入。其优点是主项目开发者可以不关注Flutter实现,不需要安装配置Flutter开发环
京东云开发者 京东云开发者
7个月前
Taro 鸿蒙技术内幕系列(三) - 多语言场景下的通用事件系统设计
作者:京东零售朱天健基于Taro打造的京东鸿蒙APP已跟随鸿蒙Next系统公测,本系列文章将深入解析Taro如何实现使用React开发高性能鸿蒙应用的技术内幕背景在鸿蒙生态系统中,虽然原生应用通常基于ArkTS实现,但在实际研发过程中发现,使用C可以显
程序员一鸣 程序员一鸣
1个月前
鸿蒙开发:如何更新对象数组
关于对象数组中的数据更新,目前例举了三种方式,一种是传统的装饰器方式,另外两种是针对数据源进行操作,数据源直接赋值的方式,适合简单、高频的单元素修改,性能最优且类型安全,而splice方法适合复杂操作或需保持引用稳定的场景,但需注意性能损耗,在实际的开发中可以根据需求,选择自己适合的方式。
飞龙AI 飞龙AI
1个月前
DevEcoStudio 中使用模拟器时如何过滤日志
DevEcoStudio中使用模拟器时如何过滤日志鸿蒙核心技术鸿蒙开发者工具DevEcoStudio在HilogSettingsFilter设置Logmessage:A03d00/JSAPP当你看到不断更新的日志时,你会不会崩溃因为Nofilters模式下
陈杨 陈杨
9小时前
鸿蒙5开发宝藏案例分享---Web页面内点击响应时延分析
当然可以!下面是一篇详细、易懂的文章,结合鸿蒙官方案例和实际代码,帮你深入理解Web加载完成时延的优化技巧👇🚀鸿蒙开发宝藏:Web加载完成时延优化实战(附代码解析)大家好呀!今天在翻鸿蒙开发者文档时,发现了一个隐藏的​​性能优化宝藏区​​——官方竟然悄
陈杨 陈杨
9小时前
鸿蒙5开发宝藏案例分享---Web加载时延优化解析
当然可以!下面是一篇详细、易懂的文章,结合鸿蒙官方案例和实际代码,帮你深入理解Web加载完成时延的优化技巧👇🚀鸿蒙开发宝藏:Web加载完成时延优化实战(附代码解析)大家好呀!今天在翻鸿蒙开发者文档时,发现了一个隐藏的​​性能优化宝藏区​​——官方竟然悄
程序员一鸣 程序员一鸣
9小时前
鸿蒙开发:如何更新对象数组
关于对象数组中的数据更新,目前例举了三种方式,一种是传统的装饰器方式,另外两种是针对数据源进行操作,数据源直接赋值的方式,适合简单、高频的单元素修改,性能最优且类型安全,而splice方法适合复杂操作或需保持引用稳定的场景,但需注意性能损耗,在实际的开发中可以根据需求,选择自己适合的方式。