06 — 窗口管理实战:沉浸式布局与避让区域处理
一、引言
多设备应用需要精确管理窗口状态,包括尺寸变化、避让区域(状态栏、导航栏、挖孔屏、手势区、键盘)和全屏切换。本文以WindowUtil工具类为核心,分析 HarmonyOS 窗口管理的最佳实践。
二、WindowUtil 单例设计
采用单例模式,全局唯一的窗口管理器:
export class WindowUtil { private static instance: WindowUtil | undefined = undefined; static getInstance(): WindowUtil { if (!WindowUtil.instance) { WindowUtil.instance = new WindowUtil(); } return WindowUtil.instance; } }三、WindowInfo 状态模型
使用@ObservedV2和@Trace装饰器,使窗口状态变化可被 UI 响应式监听:
@ObservedV2 export class WindowInfo { @Trace public windowStatusType: window.WindowStatusType; @Trace public isFullScreen: boolean; @Trace public orientation: window.Orientation; @Trace public windowSize: window.Size; @Trace public widthBp: WidthBreakpoint; @Trace public heightBp: HeightBreakpoint; @Trace public avoidSystem?: window.AvoidArea; // 状态栏 @Trace public avoidNavigationIndicator?: window.AvoidArea; // 导航栏 @Trace public avoidCutout?: window.AvoidArea; // 挖孔区 @Trace public avoidSystemGesture?: window.AvoidArea; // 手势区 @Trace public avoidKeyboard?: window.AvoidArea; // 键盘区 @Trace public statusBarTopVp: number; @Trace public navigationBarBottomVp: number; }四、窗口监听注册
4.1 初始化流程
在updateWindowInfo()中完成一次性初始化和监听注册:
updateWindowInfo(): void { // 1. 窗口状态 this.mainWindowInfo.windowStatusType = this.mainWindow.getWindowStatus(); this.mainWindow.on('windowStatusChange', this.onStatusTypeChange); // 2. 窗口尺寸 + 断点 this.mainWindowInfo.windowSize = { width, height }; this.mainWindowInfo.widthBp = this.uiContext.getWindowWidthBreakpoint(); this.mainWindow.on('windowSizeChange', this.onWindowSizeChange); // 3. 避让区域 this.mainWindowInfo.avoidSystem = this.mainWindow.getWindowAvoidArea(TYPE_SYSTEM); this.mainWindow.on('avoidAreaChange', this.onAvoidAreaChange); this.syncWindowInsetsVp(); }4.2 避让区域监听
五种避让区域分别监听:
public onAvoidAreaChange = (avoidOptions: window.AvoidAreaOptions) => { switch (avoidOptions.type) { case AvoidAreaType.TYPE_SYSTEM: // 状态栏 case AvoidAreaType.TYPE_CUTOUT: // 挖孔屏 case AvoidAreaType.TYPE_SYSTEM_GESTURE: // 边缘手势 case AvoidAreaType.TYPE_KEYBOARD: // 键盘 case AvoidAreaType.TYPE_NAVIGATION_INDICATOR: // 导航指示器 } this.syncWindowInsetsVp(); // 同步 vp 值 };五、全屏沉浸式设置
5.1 设置全屏布局
setFullScreen(isFullScreen: boolean): void { this.mainWindow.setWindowLayoutFullScreen(isFullScreen) .then(() => { this.mainWindowInfo.isFullScreen = isFullScreen; }); }5.2 设置系统栏属性
setWindowSystemBarProperties(props: window.SystemBarProperties): void { this.mainWindow.setWindowSystemBarProperties(props); }5.3 设置窗口方向
setWindowOrientation(orientation: window.Orientation): void { this.mainWindow.setPreferredOrientation(orientation); }六、沉浸式视频播放中的应用
在自适应沉浸式算法中,statusBarTopVp和navigationBarBottomVp用于计算视频的可视区域:
// ImmersionRules.ets 中计算可用区域 const restShowAreaHeight = windowHeight - bottomTabHeight - statusBarHeight; const restShowAreaWidth = windowWidth;七、资源释放
页面销毁时取消监听,防止内存泄漏:
release(): void { this.mainWindow.off('windowStatusChange'); this.mainWindow.off('windowSizeChange'); this.mainWindow.off('avoidAreaChange'); }八、最佳实践
- 单例模式:全局共享一个窗口管理器,避免重复监听
- @ObservedV2 + @Trace:窗口状态变化自动驱动 UI 更新
- AppStorageV2 持久化:
WindowInfo通过AppStorageV2.connect跨页面共享 - px2vp 转换:避让区域返回 px 单位,需要使用
ctx.px2vp()转换为 vp - 异常处理:所有窗口 API 调用都需要 try-catch 处理,防止异常崩溃