《悬浮工具球》二、Interface_WindowStage使用指南
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);
注意事项
- 页面路径必须在
main_pages.json中注册,否则加载失败 loadContent是异步操作,后续的窗口操作应在回调中执行- 重复调用
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();
注意事项
getMainWindowSync只能在loadContent回调完成之后调用- 如果在
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 |
在任意时机 |
| 配置子窗口 | setUIContent → moveWindowTo → resize → showWindow |
创建成功后依次调用 |
| 查找窗口 | window.findWindow(name) |
窗口已创建时 |
| 销毁窗口 | Window.destroyWindow |
onWindowStageDestroy中 |
参考文档:
更多推荐




所有评论(0)