登录页、聊天页、表单页,输入框一获焦,顶部标题飞出屏幕、底部按钮被遮住、导航栏错位。第一反应是给顶部元素加 expandSafeArea([SafeAreaType.KEYBOARD], [SafeAreaEdge.TOP]),期望它“钉”在原位。结果一点用都没有,甚至更糟。

expandSafeArea 的语义是让组件延伸到安全区域里,而不是避开安全区域。键盘弹出时,系统把键盘区域加入安全区域,加了 expandSafeArea 的组件反而会延伸进键盘覆盖的范围里。你把“避让”和“延伸”两个方向搞反了。

默认避让顶的是整个页面容器

ArkUI 页面默认开启键盘避让,输入框获焦后系统会把输入框顶到键盘上方。但这个动作是全局的:它推的是页面根容器,不是输入框所在的局部区域。顶部固定导航栏、搜索栏、背景图会跟着一起上移,用户看到的是整页跳动。

最直接的缓解方式是用 Scroll 包裹内容区域。系统顶的是 Scroll 容器,容器内部可以通过滚动把输入框带到可视区域,顶部元素虽然也会动,但用户至少能滚回去。这比给每个元素单独加安全区属性可靠得多。

typescript

Scroll() {
  Column() {
    Text('登录').fontSize(24).margin({ top: 40 })
    TextInput({ placeholder: '手机号' }).margin({ top: 200 })
    TextInput({ placeholder: '密码' }).margin({ top: 16 })
    Button('登录').margin({ top: 40 })
  }
  .width('100%')
}
.width('100%')
.height('100%')

全局避让模式要在 EntryAbility 里定

如果页面结构复杂,不想让系统自动推整页,可以在 EntryAbility 的 onWindowStageCreate 里关掉默认避让,或者切换模式:

typescript

windowStage.getMainWindow().then((win) => {
  win.setKeyboardAvoidMode(window.KeyboardAvoidMode.RESIZE)
})

RESIZE 让窗口高度在键盘弹出时变小,页面重新布局,适合 Column 加 Scroll 的自适应结构;OFFSET 保持窗口高度不变,整体上移,适合全屏沉浸式页面,但输入框可见性要自己兜底。两种模式没有绝对好坏,取决于页面根容器能不能承受一次高度变化。

自定义键盘和业务底栏是两层合同

customKeyboard 的 supportAvoidance: true 只保证输入框本身不被挡住,不保证输入框下方的业务底栏也安全。折叠屏展开时窗口高度变化和键盘测量可能先后到达,底栏可能先按旧窗口算一遍、再按新键盘算一遍,用户看到按钮跳了两次。

底栏的位置不能写死 bottom: 300vp。它应该由当前窗口高度、键盘高度、底部安全区和业务最小间距共同推导,并且把“当前布局轮次”和几何计算绑定,迟到的旧回调不再有资格修改最新位置。输入框可见和操作栏可点是两个独立的承诺,系统只替你兑现第一个。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐