鸿蒙Cordova开发踩坑记录:被软键盘“吞噬“的输入框
软键盘适配核心在于“控制权”的争夺。如果完全交给系统(Pan 模式),Web 就失去了布局控制权,只能被动位移。我们选择Resize 模式,虽然需要写更多 CSS/JS 来适配高度变化,但能换来最精确的 UI 控制和最接近原生的体验。提示:在中配置在鸿蒙侧可能不生效,建议优先使用 ArkTS API 进行控制。
摘要:在混合开发中,软键盘弹出时的界面适配一直是个老大难问题。本文记录了在 HarmonyOS Next 上,如何解决 WebView 中底部输入框被软键盘遮挡、页面布局剧烈抖动以及收起键盘后页面留白等一系列"连环坑"。
🤕 1. 问题现场
在我们的 ToDo List 模块中,底部有一个固定的输入栏用于添加新任务。
.input-bar {
position: fixed;
bottom: 0;
width: 100%;
}
当用户点击输入框尝试输入时,发生了诡异的现象:
- 遮挡:软键盘弹起,直接覆盖了输入栏,用户完全盲打。
- 抖动:在某些机型上,页面整体被顶起,顶部的导航栏被挤出了屏幕可视区域。
- 留白:收起键盘后,Webview 底部有时候会留下一块软键盘高度的空白区域,几秒后才恢复。
🔍 2. 机制探究
要解决这个问题,首先得理解 Android/鸿蒙系统对软键盘的两种处理模式:
- Resize 模式:软键盘弹起时,应用窗口高度减小。这会触发 Web 的
window.resize事件,fixed到底部的元素会随之跳动。 - Pan 模式:应用窗口高度不变,整体内容向上平移。这能保证焦点可见,但会把顶部导航栏顶出去。
在鸿蒙 ArkUI 中,默认的行为似乎介于两者之间,且 Web 组件内部还有自己的偏移逻辑。
🛠️ 3. 组合拳解决方案
单纯靠前端 CSS 或单纯靠原生配置都无法完美解决,我们需要一套"组合拳"。
3.1 第一拳:原生配置 (ArkTS)
首先,我们在 module.json5 或 EntryAbility 中,需要明确指定窗口的软键盘避让模式。
在 EntryAbility.ets 的 onWindowStageCreate 中:
import window from '@ohos.window';
onWindowStageCreate(windowStage: window.WindowStage) {
windowStage.getMainWindow().then((win) => {
// 设置键盘避让模式为 Resize,让 Webview 感知高度变化
win.getUIContext().setKeyboardAvoidMode(window.KeyboardAvoidMode.RESIZE);
console.info('Succeeded in setting keyboard avoid mode.');
});
}
注:虽然 Pan 模式更简单,但它会导致顶部 TitleBar 消失,对于沉浸式应用体验不佳,所以我们选择 Resize。
3.2 第二拳:Web 前端视口适配
在 Resize 模式下,position: fixed; bottom: 0 的元素会随着视口变小而自动跳到键盘上方。这本来是好事,但在 iOS 和部分 Android WebView 上,键盘弹起动画和 Web 重绘不同步,导致输入栏"闪烁"或"瞬移"。
我们采用 Flex 布局 + 中间滚动区域 的方案来替代 fixed 定位:
/* 优化前:Body 滚动,输入栏 Fixed */
/* 优化后:Body 禁止滚动,主容器 Flex */
html, body {
height: 100%;
overflow: hidden; /* 关键 */
}
.app-container {
display: flex;
flex-direction: column;
height: 100%;
}
.header {
flex: 0 0 50px;
}
.scroll-content {
flex: 1;
overflow-y: auto; /* 内容区域独立滚动 */
-webkit-overflow-scrolling: touch;
}
.input-bar {
flex: 0 0 60px;
/* 不再使用 position: fixed */
}
3.3 第三拳:JS 焦点监听与补偿
为了防止输入框在极少数情况下依然被遮挡(例如第三方输入法自带工具栏),我们添加一个 JS 监听器,在键盘弹出后强制将输入框滚动到可视区域。
const input = document.getElementById('taskInput');
// 监听 Focus 而不是 Click,适配 Tab 键切换焦点
input.addEventListener('focus', () => {
// 延迟 300ms 等待键盘动画完成
setTimeout(() => {
input.scrollIntoView({
behavior: 'smooth',
block: 'center'
});
}, 300);
});
// 监听 Window Resize 确保布局归位
window.addEventListener('resize', () => {
if (document.activeElement.tagName === 'INPUT') {
document.activeElement.scrollIntoViewIfNeeded();
}
});
🐛 4. 遇到的深坑:虚拟导航栏
在部分没有实体按键的鸿蒙手机上,底部还有一层"虚拟导航栏"(手势提示条)。
当软键盘收起时,window.innerHeight 发生变化,但有时候 Webkit 内核计算有误,认为键盘还在,导致底部留白。
修复代码:
// 监听软键盘收起事件(通过 resize 变大来判断)
let lastHeight = window.innerHeight;
window.addEventListener('resize', () => {
const currentHeight = window.innerHeight;
// 此时视口变大了,说明键盘收起了
if (currentHeight > lastHeight) {
// 强制触发一次重排,修复留白
document.body.style.height = currentHeight + 1 + 'px';
setTimeout(() => {
document.body.style.height = '100%';
}, 50);
}
lastHeight = currentHeight;
});
✅ 5. 最终效果
经过上述改造:
- 稳定性:输入栏紧贴软键盘上方,无跳动。
- 完整性:顶部导航栏始终可见,不会被顶出屏幕。
- 兼容性:适配了华为自带输入法、搜狗输入法等多种高度不一的键盘。
📚 6. 总结
软键盘适配核心在于 “控制权” 的争夺。
- 如果完全交给系统(Pan 模式),Web 就失去了布局控制权,只能被动位移。
- 我们选择 Resize 模式,虽然需要写更多 CSS/JS 来适配高度变化,但能换来最精确的 UI 控制和最接近原生的体验。
提示:在 manifest.json 中配置 softInputMode 在鸿蒙侧可能不生效,建议优先使用 ArkTS API 进行控制。
更多推荐
所有评论(0)