在鸿蒙(HarmonyOS)的 Stage 模型中,窗口管理是应用 UI 呈现的核心。WindowStage 是应用主窗口的容器,负责管理窗口的生命周期和 UI 页面的加载。以下是关于 WindowStage 获取与窗口属性设置的基础实战:

一、 WindowStage 的获取方式

在应用开发中,获取 WindowStage 实例主要有两种场景和方式:

1. 在 UIAbility 生命周期中获取(推荐)

在 UIAbility 的 onWindowStageCreate 回调中,系统会直接传入 windowStage 实例。这是最标准、最安全的获取方式,通常用于加载首页和监听窗口生命周期事件。

import { UIAbility } from '@kit.AbilityKit';
import { window } from '@kit.ArkUI';

export default class EntryAbility extends UIAbility {
    onWindowStageCreate(windowStage: window.WindowStage): void {
        // 1. 加载主页面
        windowStage.loadContent('pages/Index', (err) => {
            if (err.code) {
                console.error('加载页面失败:', err);
                return;
            }
        });
        
        // 2. 订阅窗口生命周期事件(如前后台切换、获焦/失焦)
        windowStage.on('windowStageEvent', (data: window.WindowStageEventType) => {
            switch (data) {
                case window.WindowStageEventType.SHOWN:
                    console.info('窗口进入前台');
                    break;
                case window.WindowStageEventType.HIDDEN:
                    console.info('窗口进入后台');
                    break;
            }
        });
    }
}
2. 在 Page 页面中获取(跨组件传递)

如果需要在具体的 ArkUI 页面(如 Index.ets)中获取 WindowStage(例如为了创建子窗口),可以通过 AppStorage 进行跨组件传递,或者通过 UIAbilityContext 获取。

// 在 Ability 中存入 AppStorage
onWindowStageCreate(windowStage: window.WindowStage): void {
    AppStorage.setOrCreate('windowStage', windowStage);
    windowStage.loadContent('pages/Index');
}

// 在 Page 中获取
@Entry
@Component
struct Index {
    aboutToAppear() {
        let windowStage = AppStorage.get<window.WindowStage>('windowStage');
        // 或者通过 Context 获取:
        // let context = getContext(this) as common.UIAbilityContext;
        // let ws = context.windowStage;
    }
}

二、 获取主窗口并设置属性

WindowStage 是窗口管理器,要修改窗口的具体属性(如全屏、沉浸式、透明度等),需要先通过它获取主窗口(Main Window)对象。

onWindowStageCreate(windowStage: window.WindowStage): void {
    // 获取主窗口实例
    windowStage.getMainWindow((err, mainWindow) => {
        if (err.code) {
            console.error('获取主窗口失败:', err);
            return;
        }
        
        // 设置窗口属性,例如:全屏显示
        mainWindow.setWindowLayoutFullScreen(true, (err) => {
            if (err.code) {
                console.error('设置全屏失败:', err);
            }
        });
    });
}

三、 自由窗口尺寸限制配置

对于支持自由窗口模式的设备(如平板、PC),开发者可以通过配置文件限制窗口的大小范围,防止布局因过度拉伸而错乱。

在 module.json5 的 abilities 标签中进行配置:

{
  "module": {
    "abilities": [
      {
        "name": "EntryAbility",
        "minWindowWidth": 320,      // 最小宽度 (vp)
        "minWindowHeight": 240,     // 最小高度 (vp)
        "maxWindowWidth": 1440,     // 最大宽度 (vp)
        "maxWindowHeight": 900,     // 最大高度 (vp)
        "minWindowRatio": 0.5,      // 最小宽高比
        "maxWindowRatio": 2         // 最大宽高比
      }
    ]
  }
}

四、 子窗口(SubWindow)的创建与管理

除了主窗口,应用还可以通过 WindowStage 创建子窗口,常用于实现弹窗、悬浮窗或辅助面板。子窗口的生命周期依附于主窗口。

// 在 WindowStage 中创建子窗口
windowStage.createSubWindow('mySubWindow', (err, subWindow) => {
    if (err.code) {
        console.error('创建子窗口失败:', err);
        return;
    }
    
    // 设置子窗口位置和大小
    subWindow.moveWindowTo(300, 300);
    subWindow.resize(500, 500);
    
    // 为子窗口加载页面
    subWindow.setUIContent('pages/SubPage', (err) => {
        if (!err.code) {
            // 显示子窗口
            subWindow.showWindow();
        }
    });
});

开发建议

  1. 生命周期匹配:务必在 onWindowStageCreate 中初始化窗口,并在 onWindowStageDestroy 中释放窗口资源和取消事件监听,避免内存泄漏。
  2. 沉浸式适配:开启全屏或沉浸式后,需注意避开系统状态栏和导航栏的遮挡区域(安全区),可使用 window.getLastWindow 获取避开区域(AvoidArea)的坐标进行布局适配。
  3. 响应式布局:窗口大小改变时,应结合 ArkUI 的栅格布局(GridRow/GridCol)和断点系统(Breakpoint),实现窗口缩放时的 UI 自适应。

五、 沉浸式体验优化:状态栏与导航栏定制

当应用开启全屏布局(setWindowLayoutFullScreen(true))后,内容会延伸至状态栏和导航栏区域。为了保证视觉协调与可读性,需要动态调整系统栏的样式。

import { window } from '@kit.ArkUI';

// 在获取到主窗口 windowClass 后
let sysBarProps: window.SystemBarProperties = {
    statusBarColor: '#00000000',         // 状态栏背景透明
    navigationBarColor: '#00000000',     // 导航栏背景透明
    statusBarContentColor: '#ffffff',    // 状态栏图标/文字颜色(白色)
    navigationBarContentColor: '#ffffff' // 导航栏图标/文字颜色
};

windowClass.setWindowSystemBarProperties(sysBarProps).then(() => {
    console.info('系统栏样式设置成功');
}).catch((err: BusinessError) => {
    console.error('设置系统栏样式失败:', err);
});

六、 窗口安全与隐私保护

对于包含敏感信息(如密码输入、金融交易、私人聊天)的页面,必须防止被恶意截屏或录屏。

// 开启窗口隐私模式,系统级防截屏/录屏
let isPrivacyMode: boolean = true;
windowClass.setWindowPrivacyMode(isPrivacyMode, (err: BusinessError) => {
    if (err.code) {
        console.error('开启隐私模式失败:', err);
        return;
    }
    console.info('隐私模式已开启,当前窗口内容受保护');
});

七、 窗口属性精细化控制

除了全屏和尺寸,鸿蒙还支持对窗口的交互行为进行细粒度控制,例如禁止触摸穿透、设置窗口透明度、保持屏幕常亮等。

// 1. 设置窗口是否可触摸(常用于弹窗时屏蔽底层交互)
windowClass.setWindowTouchable(true);

// 2. 获取窗口详细属性(如当前宽高、是否聚焦等)
let properties = windowClass.getWindowProperties();
console.info(`当前窗口宽度: ${properties.windowRect.width}`);
console.info(`窗口是否可聚焦: ${properties.focusable}`);

// 3. 设置窗口背景透明度(实现毛玻璃或半透明悬浮效果)
// 注:透明度可通过 WindowProperties 或 ArkUI 的 .opacity() 结合实现

八、 进阶:多窗口协同与智能吸附(PC/大屏场景)

在鸿蒙 PC 或折叠屏设备上,多窗口协同是核心体验。通过监听窗口位置变化,可以实现类似桌面操作系统的“磁吸”效果。

import window from '@ohos.window';

// 定义吸附阈值(单位:px)
const ADSORB_THRESHOLD = 10;

// 监听子窗口位置变化
subWindow.on('windowPositionChange', async (currentPos) => {
    try {
        // 获取当前应用内所有窗口
        const windowStage = await window.getLastWindowStage();
        const allWindows = await windowStage.getWindows();
        
        let finalX = currentPos.x;
        let finalY = currentPos.y;

        // 遍历其他窗口,计算边缘距离
        for (let win of allWindows) {
            if (win.getName() === subWindow.getName()) continue;
            
            const refPos = await win.getPosition();
            const refSize = await win.getWindowSize();
            
            // 示例:右边缘吸附左边缘
            if (Math.abs(finalX + currentSize.width - refPos.x) < ADSORB_THRESHOLD) {
                finalX = refPos.x - currentSize.width;
            }
        }
        
        // 如果触发了吸附,强制更新位置
        if (finalX !== currentPos.x || finalY !== currentPos.y) {
            subWindow.setPosition(finalX, finalY);
        }
    } catch (err) {
        console.error('窗口吸附计算失败:', err);
    }
});

建议

  1. 子窗口 vs 浮层(Float Layer):官方建议,如果仅仅是应用内的弹窗、下拉菜单或提示框,优先使用 ArkUI 的控件浮层能力,而不是创建独立的子窗口。子窗口的创建开销较大,且在分屏/自由窗口模式下,控件浮层的跟随与响应性能更优。
  2. 异步操作规范:所有的窗口管理 API(如 getMainWindowsetPositionsetWindowPrivacyMode)均为异步操作(返回 Promise)。在复杂逻辑中务必使用 async/await 或 .then() 妥善处理,避免因窗口对象未初始化完成而导致的空指针异常。
  3. 资源释放:对于动态创建的子窗口,在页面销毁(aboutToDisappear)或不再需要时,必须调用 destroyWindow() 进行销毁,防止内存泄漏和僵尸窗口。
Logo

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

更多推荐