前两天 UI 设计师在群里大发雷霆,说我做出来的页面在真机上全乱套了。仔细一看确实惨不忍睹:顶部的背景图被状态栏的电池电量遮挡,底部的输入框在弹出软键盘后被顶飞到了屏幕之外,还有侧边的返回手势和页面里的侧滑控件严重冲突。这三大顽疾,是每个鸿蒙开发者都必须跨过的深水区。

处理状态栏与导航栏

要想页面全屏沉浸,首先得在 EntryAbility 里把系统栏设为透明。

typescript

import window from '@ohos.window';

onWindowStageCreate(windowStage: window.WindowStage) {
  windowStage.getMainWindow().then((win) => {
    // 设置全屏布局
    win.setWindowLayoutFullScreen(true);
    // 将状态栏和导航栏设置为透明
    win.setWindowSystemBarProperties({
      statusBarColor: '#00000000',
      navigationBarColor: '#00000000',
      statusBarContentColor: '#FFFFFF',
      navigationBarContentColor: '#FFFFFF'
    });
  });
}

但此时,你的页面内容会直接顶到屏幕最上方,被前置摄像头区域遮挡。必须获取系统栏的避让高度(AvoidArea),动态给页面加上 padding。

typescript

// 获取顶部安全区高度
let avoidArea = win.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM);
let topHeight = px2vp(avoidArea.topRect.height);
// 将 topHeight 应用到页面的 padding-top 上

极其容易忽略的警示:在折叠屏展开或者横竖屏切换时,安全区的高度是动态变化的。必须监听 win.on('avoidAreaChange') 事件,实时更新你的 padding 值。

软键盘防顶起的降维打击

这是引发用户差评最多的雷区。默认情况下,软键盘弹出会把整个页面往上推。如果你的页面里有一个长列表,整个列表会瞬间被压缩,体验极差。

必须干预系统的键盘避让模式。在 windowStage 加载完成后,设置键盘模式为 RESIZE。

typescript

win.getUIContext().setKeyboardAvoidMode(KeyboardAvoidMode.RESIZE);

设置之后,键盘弹出只会压缩当前窗口的高度,而不会平移整个页面。接着,你需要配合 Scroll 组件,并给输入框加上 scrollIntoView 的逻辑。当用户点击输入框时,代码自动把输入框滚动到键盘上方的可视区域内,而不是傻等系统去推它。

手势冲突的精准拦截

最后是侧滑返回的问题。鸿蒙系统有左右边缘侧滑返回的手势。如果你的页面里恰好有一个从侧边滑出的抽屉菜单(Drawer),这两者会直接打架。

解决方法是使用 .gesture() 的优先级控制。在抽屉组件上加上 PanGesture,并在 onActionStart 时,立即通过 event.stopPropagation() 阻止事件向上冒泡到系统的侧滑返回。

如果系统的侧滑依然生效,说明底层的窗口拦截了事件。这时候必须在页面的 onBackPress 生命周期里,拦截返回事件,并判断抽屉是否处于打开状态:

typescript

onBackPress() {
  if (this.isDrawerOpen) {
    // 如果抽屉开着,先关抽屉,不退出页面
    this.isDrawerOpen = false;
    return true; 
  }
  return false; // 抽屉关着,交给系统处理返回
}

写一个包含图文长列表的聊天页面,底部放一个输入框。将状态栏设为沉浸式,并获取安全区高度。用代码把键盘模式改为 RESIZE。在真机上测试:滑动列表,点击底部输入框,看看输入框会不会被键盘遮挡。如果页面没有明显的抖动跳跃,说明你的避让逻辑完全达标了。

Logo

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

更多推荐