HarmonyOS Interface (WindowStage) 使用指南:API全解析与实战示例

适用版本:HarmonyOS NEXT(API 23+)/ DevEco Studio 6.1+
关键词:WindowStage、createSubWindow、getMainWindow、loadContent、窗口管理API


效果

一、前言

在HarmonyOS的Stage模型中,WindowStage是窗口管理的核心接口。它充当窗口管理器的角色,负责创建主窗口、子窗口,以及管理窗口的生命周期。

无论是加载主页面、获取主窗口进行属性设置,还是创建弹窗、悬浮球等子窗口,都需要通过WindowStage接口来完成。本文将对WindowStage接口的每个方法进行详细解析,并提供可直接运行的实战示例。


二、WindowStage接口概览

2.1 接口定位

WindowStage 位于 @kit.ArkUI 模块中
完整路径:import { window } from '@kit.ArkUI';

WindowStage是系统在UIAbility.onWindowStageCreate()回调中自动创建的实例,开发者不需要手动创建。

2.2 完整API列表

方法名 返回值 功能描述
loadContent(path) void 为主窗口加载目标页面
getMainWindow() Promise<Window> 异步获取主窗口实例
getMainWindowSync() Window 同步获取主窗口实例
createSubWindow(name) Promise<Window> / void 创建子窗口
getSubWindow() Promise<Array<Window>> 获取所有子窗口列表
on('windowStageEvent') void 监听WindowStage生命周期事件
off('windowStageEvent') void 取消监听WindowStage生命周期事件
showKeyboardListener() void 显示键盘监听
hideKeyboardListener() void 隐藏键盘监听
loadContentByName(name) void 通过页面名称加载页面

三、核心API详解

3.1 loadContent — 加载主页面

这是WindowStage最常用的方法,在onWindowStageCreate中调用,为主窗口加载指定的页面。

函数签名
// Promise方式
loadContent(path: string): Promise<void>

// 带LocalStorage
loadContent(path: string, storage: LocalStorage): Promise<void>

// 回调方式
loadContent(path: string, callback: AsyncCallback<void>): void
参数说明
参数 类型 说明
path string 页面路径,相对于工程ets目录,如'pages/Index'
storage LocalStorage 可选,用于页面间数据共享
callback AsyncCallback<void> 可选,加载完成的回调
使用示例
// 基础用法(回调方式)
windowStage.loadContent('pages/Index', (err) => {
  if (err.code) {
    console.error('页面加载失败:', err.message);
    return;
  }
  console.info('页面加载成功');
});

// Promise方式
try {
  await windowStage.loadContent('pages/Index');
  console.info('页面加载成功');
} catch (err) {
  console.error('页面加载失败:', err.message);
}

// 带LocalStorage数据共享
const storage = new LocalStorage();
storage.setOrCreate('theme', 'dark');
windowStage.loadContent('pages/Index', storage);
注意事项
  1. 页面路径必须在main_pages.json中注册,否则加载失败
  2. loadContent是异步操作,后续的窗口操作应在回调中执行
  3. 重复调用loadContent会替换当前页面内容

3.2 getMainWindow / getMainWindowSync — 获取主窗口

获取WindowStage下的主窗口实例,用于设置窗口属性。

函数签名
// 异步方式
getMainWindow(): Promise<Window>

// 同步方式(推荐在loadContent回调中使用)
getMainWindowSync(): Window
使用示例
// 同步方式(推荐)
windowStage.loadContent('pages/Index', (err) => {
  if (err.code) return;

  // loadContent回调中使用同步方法
  const mainWindow: window.Window = windowStage.getMainWindowSync();

  // 设置全屏布局
  mainWindow.setWindowLayoutFullScreen(true);

  // 获取窗口属性
  const props = mainWindow.getWindowProperties();
  console.info('窗口ID:', props.id);
  console.info('窗口位置:', props.windowRect);
});

// 异步方式
const mainWindow = await windowStage.getMainWindow();
注意事项
  1. getMainWindowSync只能在loadContent回调完成之后调用
  2. 如果在loadContent之前调用,可能获取到未完全初始化的窗口对象

3.3 createSubWindow — 创建子窗口

创建应用子窗口,用于弹窗、悬浮球、画中画等场景。

函数签名
// Promise方式
createSubWindow(name: string): Promise<Window>

// 回调方式
createSubWindow(name: string, callback: AsyncCallback<Window>): void
参数说明
参数 类型 说明
name string 子窗口名称,必须唯一,后续可通过此名称查找窗口
返回值

返回Window实例,通过该实例可以设置子窗口的页面、位置、大小等属性。

完整使用流程
// 创建子窗口的标准流程
windowStage.createSubWindow('myFloatBall', (err: BusinessError, subWin: window.Window) => {
  // 1. 错误检查
  if (err.code > 0) {
    console.error('创建子窗口失败:', err.message);
    return;
  }

  try {
    // 2. 设置子窗口加载的页面
    subWin.setUIContent('subwindow/FloatPage', () => {
      // 页面加载完成的回调
      subWin.setWindowBackgroundColor('#00000000');
    });

    // 3. 设置子窗口位置(单位:px)
    subWin.moveWindowTo(800, 1500);

    // 4. 设置子窗口大小(单位:px,需要vp2px转换)
    const sizePx = this.getUIContext().vp2px(56);
    subWin.resize(sizePx, sizePx);

    // 5. 显示子窗口
    subWin.showWindow();
  } catch (error) {
    console.error('子窗口配置异常:', error);
  }
});
Promise方式
try {
  const subWin = await windowStage.createSubWindow('dialogWindow');
  subWin.setUIContent('subwindow/DialogPage');
  subWin.moveWindowTo(200, 600);
  subWin.resize(800, 600);
  subWin.showWindow();
} catch (err) {
  console.error('创建子窗口失败:', err);
}

3.4 getSubWindow — 获取所有子窗口

获取当前WindowStage下创建的所有子窗口列表。

const subWindows = await windowStage.getSubWindow();
subWindows.forEach((win) => {
  console.info('子窗口名称:', win.getWindowProperties().name);
});

3.5 on/off — 生命周期事件监听

监听和取消监听WindowStage的生命周期事件。

WindowStageEventType枚举
事件类型 说明
SHOWN 窗口首次显示
HIDDEN 窗口隐藏
ACTIVE 窗口获得焦点
INACTIVE 窗口失去焦点
使用示例
// 监听
windowStage.on('windowStageEvent', (eventType: window.WindowStageEventType) => {
  switch (eventType) {
    case window.WindowStageEventType.SHOWN:
      console.info('窗口显示');
      break;
    case window.WindowStageEventType.HIDDEN:
      console.info('窗口隐藏');
      break;
    case window.WindowStageEventType.ACTIVE:
      console.info('窗口获得焦点');
      break;
    case window.WindowStageEventType.INACTIVE:
      console.info('窗口失去焦点');
      break;
  }
});

// 取消监听
windowStage.off('windowStageEvent');

四、Window接口关键方法

WindowStage创建的Window实例提供了丰富的窗口操作方法:

4.1 窗口属性设置

方法 说明 参数
setUIContent(path) 加载页面内容 页面路径
setWindowBackgroundColor(color) 设置窗口背景色 颜色值
setWindowAlpha(alpha) 设置窗口透明度 0~1
setWindowBrightness(brightness) 设置屏幕亮度 0~1
setWindowTouchable(touchable) 设置是否可触摸 boolean

4.2 窗口位置与尺寸

方法 说明 参数
moveWindowTo(x, y) 移动窗口到指定位置 px坐标
moveWindowTo(x, y, animation) 带动画移动窗口 px坐标 + 动画参数
resize(width, height) 调整窗口大小 px尺寸
resizeAsync(width, height) 异步调整窗口大小(返回Promise) px尺寸
getWindowProperties() 获取窗口属性 返回WindowProperties

4.3 窗口显示与销毁

方法 说明
showWindow() 显示窗口
hideWindow() 隐藏窗口
destroyWindow() 销毁窗口
setWindowKeepScreenOn(isKeepScreenOn) 设置是否保持屏幕常亮

4.4 全屏与沉浸式

方法 说明
setWindowLayoutFullScreen(isFullScreen) 设置全屏布局模式
setWindowSystemBarEnable(names) 设置系统栏显示/隐藏
setWindowSystemBarProperties(props) 设置系统栏样式
getWindowAvoidArea(type) 获取避让区域

五、实战示例:创建一个带悬浮子窗口的完整应用

下面通过一个完整示例,演示WindowStage和Window API的综合使用。

5.1 项目结构

entry/src/main/ets/
├── entryability/
│   └── EntryAbility.ets       // 窗口配置入口
├── pages/
│   └── Index.ets              // 主页面
└── subwindow/
    └── InfoPanel.ets          // 子窗口-信息面板

5.2 EntryAbility.ets — 窗口初始化

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

export default class EntryAbility extends UIAbility {
  onWindowStageCreate(windowStage: window.WindowStage): void {
    // Step 1: 在loadContent之前设置AppStorage,确保组件初始化时可读取
    AppStorage.setOrCreate('windowStage', windowStage);

    // Step 2: 获取主窗口并配置
    const mainWin = windowStage.getMainWindowSync();

    // Step 3: 设置全屏
    mainWin.setWindowLayoutFullScreen(true);

    // Step 4: 获取并保存安全区域
    const topArea = mainWin.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM);
    AppStorage.setOrCreate('topInset', topArea.topRect.height);

    const bottomArea = mainWin.getWindowAvoidArea(window.AvoidAreaType.TYPE_NAVIGATION_INDICATOR);
    AppStorage.setOrCreate('bottomInset', bottomArea.bottomRect.height);

    // Step 5: 监听窗口生命周期
    windowStage.on('windowStageEvent', (type: window.WindowStageEventType) => {
      if (type === window.WindowStageEventType.ACTIVE) {
        console.info('应用获得焦点');
      }
    });

    // Step 6: 加载主页面(此时AppStorage已就绪)
    windowStage.loadContent('pages/Index', (err) => {
      if (err.code) {
        console.error('加载失败');
        return;
      }
    });
  }
}

5.3 Index.ets — 主页面(含子窗口管理)

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

@Entry
@ComponentV2
struct Index {
  @Local subWindowCreated: boolean = false;
  private windowStage: window.WindowStage = AppStorage.get('windowStage') as window.WindowStage;

  aboutToAppear(): void {
    this.showInfoPanel();
  }

  /** 创建信息面板子窗口 */
  showInfoPanel(): void {
    this.windowStage.createSubWindow('infoPanel', (err, subWin) => {
      if (err.code > 0) return;

      // 设置页面和样式
      subWin.setUIContent('subwindow/InfoPanel', () => {
        subWin.setWindowBackgroundColor('#00000000');
      });

      // 设置位置(右下角)
      subWin.moveWindowTo(600, 2000);

      // 设置大小
      const ctx = this.getUIContext();
      subWin.resize(ctx.vp2px(180), ctx.vp2px(48));

      // 显示
      subWin.showWindow();
      this.subWindowCreated = true;
    });
  }

  build() {
    Column() {
      Text('WindowStage API 实战')
        .fontSize(22)
        .fontWeight(FontWeight.Bold)
        .margin({ top: 60 })

      Text(`子窗口状态:${this.subWindowCreated ? '已创建' : '未创建'}`)
        .fontSize(14)
        .fontColor('#666666')
        .margin({ top: 16 })

      Button('获取子窗口列表')
        .margin({ top: 24 })
        .onClick(async () => {
          const subs = await this.windowStage.getSubWindow();
          console.info(`当前子窗口数量:${subs.length}`);
          subs.forEach((win) => {
            console.info(`子窗口:${win.getWindowProperties().name}`);
          });
        })

      Button('隐藏/显示子窗口')
        .margin({ top: 12 })
        .onClick(() => {
          const win = window.findWindow('infoPanel');
          if (win) {
            if (this.subWindowCreated) {
              win.hideWindow();
            } else {
              win.showWindow();
            }
            this.subWindowCreated = !this.subWindowCreated;
          }
        })
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center)
  }
}

5.4 InfoPanel.ets — 子窗口页面

@Entry
@ComponentV2
struct InfoPanel {
  @Local currentTime: string = new Date().toLocaleTimeString();

  aboutToAppear(): void {
    setInterval(() => {
      this.currentTime = new Date().toLocaleTimeString();
    }, 1000);
  }

  build() {
    Row() {
      Text('\u23F0')
        .fontSize(18)
      Text(this.currentTime)
        .fontSize(14)
        .fontColor('#333333')
        .margin({ left: 8 })
    }
    .width('100%')
    .height('100%')
    .backgroundColor('rgba(255,255,255,0.95)')
    .borderRadius(24)
    .justifyContent(FlexAlign.Center)
    .shadow({ radius: 4, color: 'rgba(0,0,0,0.1)', offsetY: 2 })
  }
}

5.5 路由配置 main_pages.json

{
  "src": [
    "pages/Index",
    "subwindow/InfoPanel"
  ]
}

六、API使用注意事项

6.1 AppStorage全局数据时序

AppStorage.setOrCreate('windowStage', windowStage)必须在loadContent之前调用。如果在loadContent回调中设置,组件初始化时@Local变量读取AppStorage.get()将返回undefined

// ❌ 错误:在loadContent回调中设置
windowStage.loadContent('pages/Index', (err) => {
  AppStorage.setOrCreate('windowStage', windowStage); // 组件已初始化,读不到!
});

// ✅ 正确:在loadContent之前设置
AppStorage.setOrCreate('windowStage', windowStage);
windowStage.loadContent('pages/Index', (err) => { ... });

6.2 窗口名称唯一性

每个子窗口的名称必须全局唯一。如果重复创建同名子窗口,会报错码1300005

// ❌ 错误:重复创建同名子窗口
windowStage.createSubWindow('myWin', ...);
windowStage.createSubWindow('myWin', ...); // 报错!

// ✅ 正确:使用不同名称
windowStage.createSubWindow('floatBall', ...);
windowStage.createSubWindow('dialogPanel', ...);

6.3 查找已有窗口

通过window.findWindow(name)可以查找已创建的窗口,避免重复创建:

let subWin: window.Window | null = null;
try {
  subWin = window.findWindow('myFloatBall');
} catch (e) {
  // 窗口不存在
}

if (!subWin) {
  // 窗口不存在,创建新的
  subWin = await windowStage.createSubWindow('myFloatBall');
}

6.4 窗口销毁时机

子窗口的生命周期跟随主窗口,但建议在onWindowStageDestroy中主动销毁子窗口:

onWindowStageDestroy(): void {
  try {
    const subWin = window.findWindow('myFloatBall');
    if (subWin) {
      subWin.destroyWindow();
    }
  } catch (e) {
    // 忽略
  }
}

6.5 异步与同步方法的选择

场景 推荐方法 原因
loadContent回调中获取主窗口 getMainWindowSync() 回调中窗口已就绪,同步获取更高效
页面中异步获取窗口 getMainWindow() 页面中窗口状态不确定,异步更安全
创建子窗口 createSubWindow() 回调 子窗口创建后立即配置属性
调整窗口大小 resizeAsync() 返回Promise,支持async/await

七、常见错误码

错误码 说明 解决方案
1300001 窗口不存在 检查窗口名称是否正确
1300002 窗口操作无效 确保在正确的生命周期中调用
1300005 子窗口名称重复 使用唯一的子窗口名称
1300006 窗口未找到 确认子窗口已创建后再查找

八、总结

WindowStage是HarmonyOS窗口开发的基石。掌握其核心API的使用方式,是实现各类窗口场景的基础。

API速查表

开发场景 使用的API 调用顺序
加载主页面 loadContent 最先调用
获取主窗口 getMainWindowSync loadContent回调中
设置全屏 Window.setWindowLayoutFullScreen 获取主窗口后
获取安全区 Window.getWindowAvoidArea 设置全屏后
创建子窗口 createSubWindow 在任意时机
配置子窗口 setUIContentmoveWindowToresizeshowWindow 创建成功后依次调用
查找窗口 window.findWindow(name) 窗口已创建时
销毁窗口 Window.destroyWindow onWindowStageDestroy中

参考文档

Logo

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

更多推荐