我是兰瓶Coding,一枚刚踏入鸿蒙领域的转型小白,原是移动开发中级,如下是我学习笔记《零基础学鸿蒙》,若对你所有帮助,还请不吝啬的给个大大的赞~

1 窗口体系概览

  • 主窗(Main Window):每个 WindowStage 挂载 1 个主窗,承载页面路由与绝大多数 UI。
  • 子窗(Sub Window / Auxiliary Window):附着于主窗之上,用于弹出层、工具条、画中画、并行视图等。
  • 全局悬浮窗(Global Floating Window):跨应用叠加显示,常用于系统级工具球、录屏控件等(强权限、严格 UX 规范)。
  • 沉浸式(Immersive / Fullscreen):隐藏或半透明系统栏,扩大内容可视区域(需遵循安全与可达性限制)。

2 获取主窗:WindowStage → MainWindow

AbilityonWindowStageCreate(windowStage: WindowStage) 生命周期中获取并初始化主窗。

import window from '@ohos.window';

export default class MainAbility extends UIAbility {
  onWindowStageCreate(windowStage: window.WindowStage) {
    // 1) 获取主窗(异步/回调两种形态,以下为 Promise 形态示例)
    windowStage.getMainWindow().then((mainWin: window.Window) => {
      // 2) 配置主窗属性(尺寸、沉浸、系统栏等)
      mainWin.setWindowFocusable(true);
      // 例:设置窗口模式(全屏/分屏/浮窗,具体枚举以 SDK 为准)
      // mainWin.setWindowMode(window.WindowMode.FULLSCREEN);
      // 例:设置内容布局是否延伸至系统栏区域
      // mainWin.setWindowLayoutFullScreen(true);
      // 3) 监听窗口事件
      mainWin.on('windowSizeChange', (size) => {
        // 自适应布局
      });
      mainWin.on('avoidAreaChange', (area) => {
        // 处理刘海/圆角/系统栏避让
      });
    });
  }
}

要点

  • getMainWindow() 可能为 Promise 或回调,注意 SDK 版本差异。
  • 监听 windowSizeChange/avoidAreaChange/keyboardHeightChange 做自适应。
  • 使用 WindowStage.setUIContent(或等效)挂载根路由页面。

3 创建子窗:Overlay / Tooling / Sidecar

子窗附着于主窗,适合工具条、悬浮面板、画中画等局部场景。子窗不应滥用跨应用叠加能力。

async function createSubWindow(parent: window.Window) {
  // 1) 构造子窗参数(名称、类型、尺寸、对齐、层级)
  const cfg: window.SubWindowOptions = {
    name: 'tool_panel',
    // type/flags 以 SDK 实际枚举为准,例如:TOOL_WINDOW / FLOATING_SUB_WINDOW
    // type: window.WindowType.FLOATING_SUB_WINDOW,
    width: 360,
    height: 420,
    // 例:相对父窗的定位
    x: 24,
    y: 80,
    focusable: true,
    touchable: true
  };

  // 2) 创建并显示
  const subWin = await parent.createSubWindow(cfg);
  await subWin.setUIContent('pages/ToolPanel'); // 载入 ETS 页面
  await subWin.show();

  // 3) 生命周期与通信
  subWin.on('windowClose', () => {
    // 清理资源
  });
  // 父子窗通信:状态提升/事件总线/分布式数据(仅同应用)
}

子窗最佳实践

  • 尺寸与位置:遵循最小触控面积(≥48dp),不遮挡关键导航区域。
  • 焦点管理:显式 focusable,ESC/返回键关闭;非阻塞主线程。
  • 可访问性:为按钮/图标提供替代文本,支持键盘/旋钮切换。
  • 性能:避免高频重绘(动画/阴影/模糊谨慎用)。

4 创建全局悬浮窗(需高权限)

全局悬浮窗可覆盖他应用,仅适合系统工具级场景(如录屏按钮、通话气泡)。需要声明与动态申请系统悬浮权限(名称随版本可能不同,常见为 ohos.permission.SYSTEM_FLOAT_WINDOW)。

// 1) 清单声明(config.json/Module.json5)
/*
"requestPermissions": [
  { "name": "ohos.permission.SYSTEM_FLOAT_WINDOW", "reason": "用于提供跨应用的全局工具球" }
]
*/

// 2) 运行时申请(示例)
import abilityAccessCtrl, { Permissions } from '@ohos.abilityAccessCtrl';

async function ensureFloatPermission(context: Context): Promise<boolean> {
  const atManager = abilityAccessCtrl.createAtManager();
  const perms: Permissions[] = ['ohos.permission.SYSTEM_FLOAT_WINDOW'];
  // 查询 & 申请
  const grantStatus = await atManager.requestPermissionsFromUser(context, perms);
  return grantStatus?.[0] === 0; // 0 表示授予(以实际返回为准)
}

// 3) 创建全局悬浮窗
import window from '@ohos.window';

async function createGlobalFloat(context: Context) {
  const ok = await ensureFloatPermission(context);
  if (!ok) { return; }

  const opt: window.WindowOptions = {
    name: 'global_bubble',
    // type: window.WindowType.SYSTEM_FLOAT,      // 具体枚举以 SDK 为准
    // flags: window.WindowFlag.TOUCHABLE | window.WindowFlag.FOCUSABLE,
    width: 72,
    height: 72
  };

  const floatWin = await window.createWindow(context, opt);
  await floatWin.setUIContent('pages/GlobalBubble');
  await floatWin.setWindowKeepScreenOn(false); // 严禁无必要常亮
  await floatWin.show();

  // 拖拽/吸附边缘/单击收起等交互逻辑在 ETS 页面中实现
}

全局悬浮窗 UX & 合规

  • 使用场景极度克制:仅系统工具型、短时辅助。不做广告/误导点击/信息遮挡。
  • 尺寸:小而不遮挡(如 56–72dp),提供可拖拽快速关闭
  • 前台服务提示:在任务栏/通知中心明确展示正在运行的全局控件。
  • 隐私:不得截取他应用隐私内容;不得诱导权限;严控采集与上报频率。
  • 多显示器/横竖屏:保持位置与可达性一致。

5 沉浸式(Immersive)条件、限制与最佳实践

典型场景:视频、游戏、阅读/相册、导航/地图、AR/全景。
不建议:金融交易、账号安全、信息输入等需要系统栏/返回链路明确的场景。

function enableImmersive(win: window.Window) {
  // 关键能力(示例名):
  // 1) 内容铺满:不被系统栏裁切
  // win.setWindowLayoutFullScreen(true);

  // 2) 系统栏外观与可见性:隐藏或半透明(需考虑安全手势)
  // win.setSystemBarProperties({
  //   statusBarColor: '#00000000', // 透明
  //   statusBarContentColor: 'light', // 图标文字亮色
  //   navigationBarVisibility: false
  // });

  // 3) 手势回退/系统唤出:保持边缘滑动召回能力
}

最佳实践

  • 可逃生通道:边缘滑动可呼出系统栏;提供明确的“退出全屏”区域或手势。
  • 安全区自适配:监听 avoidAreaChange,对齐刘海/圆角/打孔区。
  • 交互密度:全屏下减少细小点击目标,优先手势/大按钮/语音。
  • 亮度/功耗:长时沉浸内容动态调暗 UI 背景,降低常亮负载。

6 权限申请与 UX 规范清单

常见权限(按需最小化)

  • ohos.permission.SYSTEM_FLOAT_WINDOW:全局悬浮窗(强管控)。
  • ohos.permission.MICROPHONE/CAMERA/LOCATION:若悬浮窗含录音/拍摄/定位控件(强提示、可撤回)。
  • ohos.permission.KEEP_SCREEN_ON(如有):仅在导航/视频播放场景短时启用。

UX 规范

  • 透明度与阴影:保证可读性但避免重绘过载;动画≤200ms。
  • 手势冲突:避让系统边缘手势;拖拽采用“按下-跟随-吸附”三段式。
  • 可达性:所有控件≥48dp;支持屏幕阅读器与硬件旋钮/方向键。
  • 多窗口协同:主窗在前台时,子窗/浮窗不应强制抢焦点。

7 性能与电量评估

7.1 性能基线

  • 窗口数量上限:控制并发子窗数量(建议≤3),浮窗仅 1 个活跃实例。
  • 合成与重绘:减少半透明/阴影/模糊;动画使用硬件加速属性(位移/缩放/透明度),避免逐帧重排。
  • 渲染帧率:静态 UI 锁 30fps;动画期间临时升至 60fps 后回落。
  • 事件合并:高频拖拽/滚动做节流/合帧(如 16–32ms 合并)。

7.2 电量优化

  • 可见性驱动onForeground/onBackgroundwindowVisibilityChange 中暂停动画/定时器/传感器。
  • 刷新策略:浮窗处于静止时停止重绘;仅状态变更时 requestRender().
  • 定位与传感:非导航场景禁用持续定位;使用“显著位置变化”与低频策略。
  • 网络:合并上报、压缩与去抖;夜间与后台使用更长同步间隔。
  • 屏幕常亮:仅在必要场景短时启用,随场景退出立即恢复系统策略。

建议指标

  • 主/子/浮窗 平均 CPU < 8% / 峰值 < 20%(中端机型参考)。
  • 浮窗空闲 GPU 占用≈0%;拖拽期间平均帧时 ≤16.6ms
  • 后台 10 分钟功耗增量 < 1%(不含导航/视频)。

8 调试与可观测性

  • 指标埋点:窗口创建/显示/隐藏耗时、帧时 P50/P95、掉帧率、合成层数、权限弹框点击率。
  • 日志分级:窗口生命周期、权限申请结果、异常捕获(含用户拒绝/撤回)。
  • A/B 验证:沉浸式开关、浮窗尺寸/位置、拖拽吸附算法。
  • 故障注入:模拟权限撤回、分辨率变化、旋转/多显示器切换。

9 常见陷阱与规避

  • 过度使用全局悬浮窗:被系统或应用市场拒绝;请退回子窗或通知样式。
  • 遮挡系统手势与导航:造成“无法返回/退出”的糟糕体验;务必避让与提供退出通道。
  • 频繁重建窗口:应复用窗口实例,使用显示/隐藏管理生命周期。
  • 忽视可达性:缺少替代文本/焦点循环/硬件控制支持,影响无障碍审核。
  • 亮度与常亮滥用:显著增加功耗,影响续航。

10 小结与落地清单

  • WindowStage.getMainWindow() 获取并初始化主窗,订阅尺寸/避让区变化。
  • ✅ 子窗用于同应用的工具/面板,控制数量、尺寸与焦点。
  • ✅ 全局悬浮窗仅限系统级短时辅助场景,严格权限与 UX 规范
  • ✅ 沉浸式仅用于内容消费/导航类,保留可逃生手势与安全区适配。
  • ✅ 建立性能+功耗基线与可观测性,按可见性暂停无效渲染与传感。

(未完待续)

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐