HarmonyOS 鸿蒙Next 如何定位键盘弹出后原UI布局错乱问题 鸿蒙场景化案例
HarmonyOS 鸿蒙Next 如何定位键盘弹出后原UI布局错乱问题 鸿蒙场景化案例
【问题现象】
在键盘弹出时,用户界面布局出现错乱现象,比如键盘遮挡输入框、Tab被键盘顶起、导航栏被顶出屏幕等问题。
效果如下:
上图为初始状态,下图为错误的场景:
【背景知识】
- 软键盘布局适配场景介绍,键盘避让机制。 参考链接:键盘布局适配
- layoutWeight:组件在父组件剩余空间的布局权重。 参考链接:layoutWeight
- expandSafeArea:控制组件扩展其安全区域。 参考链接:expandSafeArea
【定位思路】
键盘的避让模式 KeyboardAvoidMode 默认为OFFSET,此时键盘的避让效果为整体页面被顶起,符合问题现象。
【解决方案】
1、使用RESIZE键盘避让模式
设置 KeyboardAvoidMode值为 RESIZE,此时页面中设置百分比宽高的组件会跟随页面压缩,直接设置宽高的组件会按设置的固定大小布局。
示例中 List 容器使用 layoutWeight(1),layoutWeight(number) 属性作用为组件在父组件主轴方向的布局权重,即占父组件容器在主轴方向上剩余区域的多少,自适应占满父组件剩余空间。此时键盘弹出时 List 组件高度会被压缩,其他组件的高度不变。而在 RESIZE 模式下,expandSafeArea 设置不生效。
效果如下:最下方 “Red Square Below” 被键盘顶起,List 组件高度被压缩。
2、键盘弹出时控制组件是否避让
当不需要被顶起的组件被键盘顶起时,可通过 expandSafeArea使该组件不避让键盘。需要注意,在 KeyboardAvoidMode.RESIZE模式下,expandSafeArea属性不生效。
expandSafeArea 意为扩大安全区域,KEYBOARD事件下,扩展TOP、BOTTOM区域,即该组件可以渲染到键盘的上下区域,此时该组件不避让键盘。
代码示例如下::
Blank() { // Blank为任意UI组件
}.expandSafeArea([SafeAreaType.KEYBOARD], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM]) // 意为该组件可以渲染到键盘遮盖的区域
效果如下:
上图中,底部的 BottomTextArea 未设置键盘避让,因此输入时仅该组件被顶起,其余组件未被顶起。
下图中,List 设置了键盘避让,此时最下方的 TextInput 组件输入时,该输入组件被键盘遮挡。
3、通过监听键盘高度或安全区域高度变化,手动处理UI布局
手动处理 UI 布局时,要注意容器组件的高度是否为固定高度,推荐使用 layoutWeight属性自适应容器或组件高度。
当页面释放时,要注意关闭键盘事件监听。
代码示例如下:
aboutToAppear(): void {
this.setKeyboardHandler();
}
aboutToDisappear(): void {
// 关闭键盘高度监听,避免内存泄露
this.keyboardListenerWindow && this.keyboardListenerWindow.off('keyboardHeightChange');
}
setKeyboardHandler() {
window.getLastWindow(getContext(this)).then(currentWindow => {
this.keyboardListenerWindow = currentWindow;
currentWindow.on('keyboardHeightChange', (height:number) => {
this.keyboardHeight = px2vp(height);
// 后续根据键盘弹出的高度,自行计算组件布局。
})
})
}
效果如下:
图一:Bottom TextArea 输入时,最底部 “Red Square Below” 被顶起。
图二:列表中奇数位输入框输入时,顶起位置为底部 “Red Square Above”。
图三:列表中偶数位输入框输入时,顶起位置为 List 底部,即 List 下方 “Red Square Above” 被键盘遮挡。
【总结】
- KeyboardAvoidMode.RESIZE 模式下,expandSafeArea 设置将不起作用。
- 容器组件推荐使用百分比宽高度设置,如 .width('100%'),或使用 layoutWeight(1)。
- 列表类容器内的子组件若使用 expandSafeArea,需要进行嵌套。因此,不推荐对列表容器内的子组件使用此属性。
- 使用各种监听时,务必在退出页面前取消监听,以避免潜在的内存泄漏。
- 在自定义页面顶起高度时,可以在容器最底部使用 Blank 组件,并根据需要被顶起的高度结合 animateTo 显式动画设置 Blank 组件的高度,以实现页面顶起效果。