鸿蒙新特性:@ohos.window 窗口管理 API 实战
引言
窗口是应用与用户交互的载体。在 HarmonyOS 应用开发中,窗口管理不仅关乎界面呈现,还涉及亮度控制、屏幕方向、折叠屏适配等实用场景。@ohos.window 模块提供了完整的窗口管理能力,让开发者可以在运行时查询和操控窗口状态。
本文从 API 架构、核心用法到实战 Demo,全面解析 @ohos.window 的窗口管理能力。所有代码基于 HarmonyOS NEXT API 24,DevEco Studio 6.1.1 实测通过。
@ohos.window 模块概览
导入方式
import window from '@ohos.window';
从 @kit.ArkUI 默认导入,与 @ohos.curves、@ohos.process 等模块的导入方式一致。无需声明任何权限。
核心架构
模块的核心是一个三层架构:
| 层次 | 入口/对象 | 职责 |
|---|---|---|
| 获取窗口 | window.getLastWindow(ctx) |
返回当前页面对应的 Window 实例 |
| 窗口操作 | Window 实例 |
查属性、设亮度、设方向、监听事件 |
| 数据结构 | WindowProperties、Orientation 等 |
承载窗口元数据 |
整个流程可以概括为:先通过 getLastWindow() 拿到窗口实例,再调用实例方法操作窗口。由于 getLastWindow() 返回 Promise,实际开发中需要处理异步逻辑。
核心 API 详解
1. getLastWindow(ctx) — 获取窗口实例
window.getLastWindow(getContext())
.then((win: window.Window) => {
// 窗口实例就绪,可以调用后续方法
})
.catch(() => {
// 获取失败,可能是上下文类型不匹配
});
参数:ctx 是当前页面的上下文,通过 getContext() 获取。在 API 24 中,ARKUI 页面的 getContext() 可以直接传递给 getLastWindow()。
返回值:Promise<Window>。在 .then() 回调中拿到 Window 实例后,即可调用窗口的所有方法。
注意:getLastWindow() 获取的是当前 UIAbility 的最后显示窗口。在多窗口场景下,这通常是你正在交互的那个窗口。
2. getWindowProperties() — 读取窗口属性
const prop: window.WindowProperties = win.getWindowProperties();
WindowProperties 对象包含以下关键字段:
| 字段 | 类型 | 说明 |
|---|---|---|
type |
WindowType |
窗口类型:标准模式(0)、全屏(1)、分屏(2)、悬浮窗(3)、最小化(4) |
brightness |
number |
窗口亮度,0~1,-1 表示跟随系统调节 |
isFullScreen |
boolean |
是否为全屏状态 |
isLayoutFullScreen |
boolean |
是否为沉浸式全屏(状态栏和导航栏隐藏) |
这些属性是同步获取的,不需要 Promise,调用后立即返回。
3. setWindowBrightness(brightness: number) — 调节亮度
win.setWindowBrightness(0.7)
.then(() => { /* 设置成功 */ })
.catch(() => { /* 设置失败,可能需要系统权限 */ });
参数:brightness 范围为 0.01 ~ 1.0。设为 -1 表示恢复系统自动调节。
返回值:Promise<void>。这意味着亮度设置是一个异步操作,需要在 Promise 中处理成功/失败情况。
限制:在某些设备上,应用可能没有权限修改系统亮度,此时调用会进入 .catch() 分支。因此建议使用时配合 try-catch 或 .catch() 做降级处理。
4. setPreferredOrientation(orientation: Orientation) — 设置方向
import window from '@ohos.window';
// 设置为竖屏
win.setPreferredOrientation(window.Orientation.PORTRAIT);
// 设置为横屏
win.setPreferredOrientation(window.Orientation.LANDSCAPE);
// 恢复自动旋转
win.setPreferredOrientation(window.Orientation.AUTO_ROTATION);
参数:window.Orientation 枚举,常用值如下:
| 枚举值 | 含义 |
|---|---|
Orientation.PORTRAIT |
强制竖屏(向上) |
Orientation.PORTRAIT_INVERTED |
强制竖屏(倒转) |
Orientation.LANDSCAPE |
强制横屏(左转) |
Orientation.LANDSCAPE_INVERTED |
强制横屏(右转) |
Orientation.AUTO_ROTATION |
跟随传感器自动旋转 |
返回值:同样返回 Promise<void>。设置方向可能受系统限制(如分屏模式下无法切换方向)。
5. on / off — 窗口事件监听
// 注册监听
win.on('windowSizeChange', (size: window.Size) => {
console.log('窗口尺寸: ' + size.width + '×' + size.height);
});
// 取消监听(页面销毁时必须)
win.off('windowSizeChange');
支持的事件类型(WindowEventType):
| 事件名 | 触发时机 |
|---|---|
windowSizeChange |
窗口尺寸发生变化(折叠屏展开/折叠、分屏调整、旋转等) |
windowStatusChange |
窗口状态变化(最大/最小化、焦点获得/失去) |
avoidAreaChange |
避让区域变化(状态栏、导航栏、刘海屏等) |
最佳实践:在 aboutToAppear 中注册监听,在 aboutToDisappear 中取消监听,避免内存泄漏。
实战 Demo:窗口管理实验室
下面实现一个完整的窗口管理工具页面,包含 4 个功能区域:
- 窗口属性展示 — 实时显示方向、模式、亮度、全屏状态
- 亮度调节 — Slider 滑块 + 应用按钮
- 方向控制 — 三按钮切换竖屏/横屏/自动旋转
- 事件日志 — 监听窗口尺寸变化并记录历史
完整代码
import { router } from '@kit.ArkUI';
import window from '@ohos.window';
import { FontSize, Spacing } from '../common/Constants';
interface EventLog {
time: string;
msg: string;
}
@Entry
@Component
struct WindowLabPage {
@State orientation: string = '加载中...';
@State winBrightness: number = 0;
@State winType: string = '加载中...';
@State isFullScreen: string = '加载中...';
@State brightnessSlider: number = 0.5;
@State orientationIndex: number = 2;
@State eventLogs: EventLog[] = [];
@State loading: boolean = true;
private win: window.Window | null = null;
aboutToAppear(): void {
window.getLastWindow(getContext())
.then((w: window.Window) => {
this.win = w;
this.loadProperties();
this.setupListener();
this.loading = false;
})
.catch(() => {
this.orientation = '获取失败';
this.winType = '获取失败';
this.loading = false;
this.addLog('窗口获取失败,请检查 Context');
});
}
aboutToDisappear(): void {
if (this.win !== null) {
this.win.off('windowSizeChange');
}
}
private loadProperties(): void {
if (this.win === null) {
return;
}
try {
const prop: window.WindowProperties = this.win.getWindowProperties();
this.winType = this.typeName(prop.type);
this.isFullScreen = prop.isFullScreen ? '是' : '否';
this.winBrightness = prop.brightness;
this.brightnessSlider = prop.brightness < 0 ? 0.5 : prop.brightness;
this.orientation = '自动旋转';
this.orientationIndex = 2;
} catch (e) {
this.orientation = '获取失败';
}
}
private typeName(t: number): string {
switch (t) {
case 1: return '全屏模式';
case 2: return '分屏模式';
case 3: return '悬浮窗模式';
case 4: return '最小化模式';
default: return '标准模式 (' + t + ')';
}
}
private setupListener(): void {
if (this.win === null) {
return;
}
try {
this.win.on('windowSizeChange', (size: window.Size) => {
this.addLog('窗口尺寸变更: ' + size.width + 'x' + size.height);
this.loadProperties();
});
this.addLog('窗口事件监听已启动');
} catch (e) {
this.addLog('事件监听注册失败');
}
}
private addLog(msg: string): void {
const now: Date = new Date();
const ts: string = now.getHours().toString().padStart(2, '0') + ':' +
now.getMinutes().toString().padStart(2, '0') + ':' +
now.getSeconds().toString().padStart(2, '0');
const entry: EventLog = { time: ts, msg: msg };
this.eventLogs = [entry].concat(this.eventLogs).slice(0, 30);
}
private setBrightness(): void {
if (this.win === null) {
this.addLog('窗口未就绪');
return;
}
this.win.setWindowBrightness(this.brightnessSlider)
.then(() => {
this.addLog('亮度已设为 ' + this.brightnessSlider.toFixed(2));
this.winBrightness = this.brightnessSlider;
})
.catch(() => {
this.addLog('设置亮度失败(可能需要系统权限)');
});
}
private setOrientation(index: number): void {
if (this.win === null) {
this.addLog('窗口未就绪');
return;
}
this.orientationIndex = index;
let orient: window.Orientation;
let label: string;
if (index === 0) {
orient = window.Orientation.PORTRAIT;
label = '竖屏';
} else if (index === 1) {
orient = window.Orientation.LANDSCAPE;
label = '横屏';
} else {
orient = window.Orientation.AUTO_ROTATION;
label = '自动旋转';
}
this.win.setPreferredOrientation(orient)
.then(() => {
this.orientation = label;
this.addLog('方向已设为: ' + label);
})
.catch(() => {
this.addLog('设置方向失败(可能需要系统权限)');
});
}
private refreshAll(): void {
this.addLog('刷新窗口属性');
this.loadProperties();
}
build() {
Column() {
Row() {
Text('<')
.fontSize(28).fontColor('#FFFFFF')
.onClick(() => { router.back(); })
Text('窗口管理实验室')
.fontSize(FontSize.TITLE).fontColor('#FFFFFF')
.fontWeight(FontWeight.Bold).margin({ left: Spacing.MD })
Blank()
Text('@ohos.window')
.fontSize(9).fontColor('#FFFFFFCC')
}
.width('100%')
.padding({ left: Spacing.LG, right: Spacing.LG, top: 14, bottom: 14 })
.backgroundColor('#0891B2')
Scroll() {
Column() {
Text('窗口属性')
.fontSize(FontSize.CAPTION).fontColor('#0891B2')
.width('100%').margin({ bottom: 6 })
Column() {
this.propRow('当前方向', this.orientation, '#ECFEFF')
this.propRow('窗口模式', this.winType, '#FFFFFF')
this.propRow('屏幕亮度', this.winBrightness < 0 ? '跟随系统' :
this.winBrightness.toFixed(2), '#ECFEFF')
this.propRow('全屏状态', this.isFullScreen, '#FFFFFF')
if (this.loading) {
LoadingProgress()
.width(24).height(24)
.color('#0891B2')
.margin({ top: 4 })
}
}
.width('100%').padding(Spacing.MD)
.backgroundColor('#FFFFFF').borderRadius(10)
.margin({ bottom: Spacing.MD })
Text('屏幕亮度调节')
.fontSize(FontSize.CAPTION).fontColor('#0891B2')
.width('100%').margin({ bottom: 6 })
Column() {
Row() {
Slider({
value: this.brightnessSlider,
min: 0.01, max: 1.0, step: 0.01,
style: SliderStyle.InSet
})
.layoutWeight(1)
.onChange((v: number) => { this.brightnessSlider = v; })
Text((this.brightnessSlider * 100).toFixed(0) + '%')
.fontSize(16).fontColor('#0891B2')
.fontWeight(FontWeight.Bold).fontFamily('monospace')
.width(48).textAlign(TextAlign.End)
}
.width('100%')
.margin({ bottom: 10 })
Button('应用亮度')
.fontSize(13).fontColor('#FFFFFF')
.height(36).backgroundColor('#0891B2').borderRadius(8)
.width('100%')
.onClick(() => { this.setBrightness(); })
}
.width('100%').padding(Spacing.MD)
.backgroundColor('#FFFFFF').borderRadius(10)
.margin({ bottom: Spacing.MD })
Text('屏幕方向控制')
.fontSize(FontSize.CAPTION).fontColor('#0891B2')
.width('100%').margin({ bottom: 6 })
Column() {
Row() {
this.orientBtn('竖屏', 0)
this.orientBtn('横屏', 1)
this.orientBtn('自动旋转', 2)
}
.width('100%')
}
.width('100%').padding(Spacing.MD)
.backgroundColor('#FFFFFF').borderRadius(10)
.margin({ bottom: Spacing.MD })
Text('窗口事件日志')
.fontSize(FontSize.CAPTION).fontColor('#0891B2')
.width('100%').margin({ bottom: 6 })
Column() {
if (this.eventLogs.length === 0) {
Text('暂无事件')
.fontSize(12).fontColor('#94A3B8')
.width('100%').textAlign(TextAlign.Center)
.padding({ top: 12, bottom: 12 })
} else {
ForEach(this.eventLogs, (log: EventLog) => {
Row() {
Text(log.time)
.fontSize(10).fontColor('#94A3B8').fontFamily('monospace')
.width(50)
Text(log.msg)
.fontSize(11).fontColor('#0F172A')
.layoutWeight(1).maxLines(2)
.textOverflow({ overflow: TextOverflow.Ellipsis })
}
.width('100%')
.padding({ top: 5, bottom: 5 })
.border({ width: { bottom: 0.5 }, color: '#E2E8F0' })
})
}
}
.width('100%').padding(Spacing.MD)
.backgroundColor('#FFFFFF').borderRadius(10)
.margin({ bottom: Spacing.MD })
Button('刷新窗口属性')
.fontSize(13).fontColor('#FFFFFF')
.height(40).backgroundColor('#0891B2').borderRadius(10)
.width('100%')
.onClick(() => { this.refreshAll(); })
Text('@ohos.window 提供窗口管理能力。核心 API:window.getLastWindow(ctx) 获取窗口实例 → 返回 Promise<Window>。Window 对象:getWindowProperties() 获取窗口属性(类型/亮度/全屏等)、setWindowBrightness() 调节屏幕亮度(0~1)、setPreferredOrientation() 设置屏幕方向(竖屏/横屏/自动)、on/off 监听 windowSizeChange 窗口尺寸变化事件。需注意:部分操作(设置方向、亮度)受系统保护和设备限制,可能无效果或抛异常。')
.fontSize(FontSize.CAPTION)
.fontColor('#64748B')
.width('100%')
.margin({ top: Spacing.MD, bottom: Spacing.XXL })
}
.width('100%')
.padding({ left: Spacing.LG, right: Spacing.LG, top: Spacing.MD, bottom: Spacing.MD })
}
.layoutWeight(1).scrollBar(BarState.Off)
.backgroundColor('#F2F3F5')
}
.width('100%').height('100%')
.backgroundColor('#F2F3F5')
}
@Builder
propRow(label: string, value: string, bgColor: string) {
Row() {
Text(label)
.fontSize(12).fontColor('#64748B').layoutWeight(1)
Text(value)
.fontSize(13).fontColor('#0F172A')
.fontWeight(FontWeight.Bold).fontFamily('monospace')
}
.width('100%')
.padding({ left: 10, right: 10, top: 8, bottom: 8 })
.backgroundColor(bgColor).borderRadius(6)
.margin({ bottom: 4 })
}
@Builder
orientBtn(label: string, index: number) {
Text(label)
.fontSize(12)
.fontColor(this.orientationIndex === index ? '#FFFFFF' : '#0891B2')
.fontWeight(this.orientationIndex === index ? FontWeight.Bold : FontWeight.Normal)
.padding({ left: 14, right: 14, top: 8, bottom: 8 })
.borderRadius(6)
.backgroundColor(this.orientationIndex === index ? '#0891B2' : '#ECFEFF')
.margin({ right: 8 })
.onClick(() => { this.setOrientation(index); })
}
}
页面结构解析
标题栏:使用 #0891B2(cyan-600)作为主色调,与之前的实验室页面形成视觉区分。左侧返回按钮 + 标题"窗口管理实验室" + 右上角模块名。
窗口属性卡片(交互点 1 — 属性读取):展示 4 个关键属性——当前方向、窗口模式、屏幕亮度、全屏状态。加载时显示 LoadingProgress 动画,窗口就绪后通过 getWindowProperties() 同步读取所有属性。注意 brightness 字段与内置组件方法冲突,我们在代码中命名为 winBrightness。
亮度调节卡片(交互点 2 — 亮度控制):使用 SliderStyle.InSet 样式滑块,范围 0.01~1.0,步进 0.01。右侧百分比数字实时跟随滑块。点击"应用亮度"调用 setWindowBrightness(),成功后更新属性卡片中的亮度显示。
方向控制卡片(交互点 3 — 方向切换):三个按钮(竖屏/横屏/自动旋转),选中态高亮(白字 + 主题色背景),未选中态(主题色字 + 浅色背景)。基于 orientationIndex 驱动视觉状态。每个按钮调用 setPreferredOrientation(),结果在事件日志中反馈。
事件日志卡片(交互点 4 — 事件监听):在 setupListener() 中注册 windowSizeChange 监听。每次窗口尺寸变化时,将尺寸和时间戳写入日志列表。日志最多保留 30 条,最新在前。页面销毁时在 aboutToDisappear() 中 off('windowSizeChange') 清理。
关键注意点
1. brightness 属性名冲突
ArkTS 中,@State brightness: number 会与 ArkUI 内置的 .brightness() 视觉属性方法冲突,导致编译错误:
Property 'brightness' in type 'WindowLabPage' is not assignable...
Type 'number' is not assignable to type '{ (value: number): CommonAttribute; }'
解决方案:将状态变量重命名为 winBrightness 或其他不与内置属性重名的名称。

2. getContext() 的上下文限制
在 API 24 中,getContext() 在新版本 SDK 中可能被标记为 deprecated,推荐使用组件自带的 getUIContext() 方法。不过 getLastWindow() 目前仍接受 getContext() 的返回值。如果未来版本不再支持,需要改用 this.getUIContext().getHostContext() 等替代方案。
3. setPreferredOrientation 不是强制的
调用 setPreferredOrientation() 只是"建议"系统使用某个方向,不保证一定生效。例如在分屏模式下,系统可能忽略方向设定。如果设置失败,Promise 会进入 .catch(),此时应在日志中反馈而不是静默失败。
4. 页面销毁时必须取消监听
窗口事件的监听器是全局注册的,不会随页面退出自动解绑。如果不在 aboutToDisappear() 中调用 off(),当用户多次进入/退出页面时,会产生大量僵尸监听器,最终导致内存泄漏和不必要的回调触发。
5. WindowProperties 字段可用性
并非 .d.ts 中声明的所有 WindowProperties 字段都在运行时有效。部分字段(如 orientation)在当前版本 SDK 中声明存在但实际运行时属性不可读,访问会触发异常。Demo 中通过 loadProperties() 包裹 try-catch 来防御。
适用场景
- 阅读器/视频播放器:通过
setPreferredOrientation锁定横屏/竖屏 - 系统设置类应用:通过
setWindowBrightness提供亮度调节 - 折叠屏适配:通过
windowSizeChange监听窗口尺寸变化,动态调整布局 - 分屏模式调试:通过
getWindowProperties()检测当前窗口模式 - 沉浸式体验:通过
isFullScreen检测全屏状态,配合setWindowLayoutFullScreen实现沉浸式
总结
@ohos.window 是一个基础但实用的模块。它的 API 数量不多——核心就是 getLastWindow() 获取窗口,然后通过 Window 实例操作属性和注册监听——但覆盖了窗口管理的核心场景。
相比其他模块(如 @ohos.process 的零权限同步 API),窗口模块的特点是异步 Promise 风格,这要求开发者在 .then() / .catch() 中正确处理成功和失败路径。同时它涉及上下文获取和生命周期管理,是一个考验开发者工程素质的模块。
更多推荐


所有评论(0)