
前言
在 HarmonyOS 应用开发中,窗口管理是影响用户体验的关键因素之一。一个优秀的窗口管理方案可以实现沉浸式布局、透明状态栏、适配挖孔屏等效果,让应用看起来更加专业和精致。
本文将以"海风日记"App 为实战案例,深入讲解 WindowStage 窗口管理的完整体系,包括窗口创建、状态栏配置、沉浸式布局、安全区域适配以及多窗口适配等核心内容。通过本文,你将掌握在 HarmonyOS 中实现全沉浸式体验的全部技巧。
一、WindowStage 概述
1.1 什么是 WindowStage
WindowStage 是 HarmonyOS Stage 模型中用于管理应用窗口的类,它负责:
- 窗口创建:为 UIAbility 创建和管理主窗口
- 页面加载 :通过
loadContent方法加载首屏页面 - 窗口属性配置:设置状态栏、导航栏、窗口尺寸等
- 窗口事件监听:监听窗口尺寸变化、焦点变化等
1.2 WindowStage 与 MainWindow 的关系
WindowStage 和 MainWindow 是包含与被包含的关系:
| 对象 | 职责 | 获取方式 |
|---|---|---|
WindowStage |
窗口阶段管理,提供顶层 API | UIAbility 回调参数传入 |
MainWindow |
具体窗口实例,提供细粒度控制 | windowStage.getMainWindow() |
typescript
// 获取 MainWindow 的完整流程
onWindowStageCreate(windowStage: window.WindowStage): void {
// 1. 加载首屏
windowStage.loadContent('pages/SplashPage', () => {})
// 2. 获取主窗口
windowStage.getMainWindow().then((win: window.MainWindow) => {
// 3. 配置窗口属性
win.setWindowSystemBarProperties({...})
})
}
1.3 窗口的类型
HarmonyOS 支持多种窗口类型:
| 窗口类型 | 用途 | 创建方式 |
|---|---|---|
| 主窗口 (MainWindow) | 应用主界面 | UIAbility 自动创建 |
| 子窗口 (SubWindow) | 悬浮窗、弹窗 | window.createSubWindow() |
| 系统窗口 (SystemWindow) | 系统级悬浮窗 | 需要特殊权限 |
提示:普通应用只需要关注 MainWindow 即可,子窗口和系统窗口需要额外权限声明。
二、状态栏配置实战
2.1 透明状态栏实现
"海风日记"在 onWindowStageCreate 中实现了状态栏透明化:
typescript
windowStage.getMainWindow().then((win) => {
win.setWindowSystemBarProperties({
statusBarColor: '#00000000', // 完全透明
statusBarContentColor: '#2D2926' // 暖棕黑文字
})
})
透明状态栏的实现要点:
statusBarColor必须使用 8 位色值(含 Alpha 通道),#00000000表示完全透明statusBarContentColor使用 6 位色值,控制时钟、电量等图标的颜色- 透明状态栏需要配合页面布局的
padding或安全区域使用,避免内容被状态栏遮挡
2.2 状态栏颜色适配
在实际应用中,状态栏颜色需要根据页面主题动态调整:
typescript
// 浅色页面:深色状态栏文字 + 透明背景
win.setWindowSystemBarProperties({
statusBarColor: '#00000000',
statusBarContentColor: '#2D2926'
})
// 深色页面:白色状态栏文字 + 透明背景
win.setWindowSystemBarProperties({
statusBarColor: '#00000000',
statusBarContentColor: '#FFFFFF'
})
2.3 状态栏 API 完整参数
setWindowSystemBarProperties 支持的完整参数:
| 参数 | 类型 | 说明 | 示例 |
|---|---|---|---|
statusBarColor |
string | 状态栏背景色 | '#00000000' |
statusBarContentColor |
string | 状态栏内容颜色 | '#2D2926' |
navigationBarColor |
string | 导航栏背景色 | '#00000000' |
navigationBarContentColor |
string | 导航栏按钮颜色 | '#000000' |
enableStatusBarAnimation |
boolean | 状态栏变化是否动画 | true |
三、沉浸式布局实现
3.1 什么是沉浸式布局
沉浸式布局是指应用内容延伸到状态栏和导航栏区域,让用户获得更沉浸的视觉体验。"海风日记"的闪屏页(SplashPage)就是典型的沉浸式布局:
海风日记闪屏页的沉浸式布局效果,内容从屏幕顶部延伸到状态栏区域
3.2 沉浸式布局的两种实现方式
在 HarmonyOS 中,实现沉浸式布局主要有两种方式:
方式一:通过 WindowStage 配置(推荐)
typescript
// 在 EntryAbility 中配置沉浸式
onWindowStageCreate(windowStage: window.WindowStage): void {
windowStage.getMainWindow().then((win) => {
// 设置全屏布局
win.setWindowLayoutFullScreen(true)
// 透明状态栏
win.setWindowSystemBarProperties({
statusBarColor: '#00000000',
statusBarContentColor: '#2D2926'
})
})
}
方式二:通过页面组件配置
typescript
// 在页面组件中通过 expandSafeArea 扩展安全区域
@Entry
@Component
struct SplashPage {
build() {
Column()
.width('100%')
.height('100%')
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])
}
}
3.3 两种方式的对比
| 对比维度 | WindowStage 方式 | 页面组件方式 |
|---|---|---|
| 配置范围 | 全局生效 | 逐页面配置 |
| 灵活性 | 较低 | 高,可按页面定制 |
| 推荐场景 | 全应用统一沉浸式 | 部分页面需要沉浸式 |
| 代码量 | 少 | 需在每个页面添加 |
3.4 闪屏页的沉浸式实现
"海风日记"的闪屏页通过全屏渐变 + Canvas 波浪实现了沉浸式效果:
typescript
@Entry
@Component
struct SplashPage {
build() {
Stack({ alignContent: Alignment.Bottom }) {
// 全屏渐变背景
Column()
.width('100%').height('100%')
.linearGradient({
angle: 160,
colors: [
['#FFE4B0', 0.0],
['#FFD080', 0.35],
['#F5A623', 0.70],
['#C8800A', 1.0],
]
})
// Canvas 波浪(覆盖底部)
Canvas(this.ctx)
.width('100%').height(300)
.onReady(() => { this.drawWaves() })
// 品牌内容
Column({ space: 0 }) {
Text('海风日记').fontSize(42).fontColor('#FFFFFF')
Text('让每一天随海风飘散').fontSize(16)
}
}
.width('100%').height('100%')
}
}
四、安全区域适配
4.1 安全区域的概念
安全区域(Safe Area)是指屏幕中不会被系统 UI(状态栏、导航栏、刘海、挖孔)遮挡的可安全显示内容的区域。在鸿蒙设备上,安全区域因设备而异:
| 设备类型 | 安全区域影响因素 |
|---|---|
| 普通手机 | 状态栏 + 导航栏(虚拟按键) |
| 挖孔屏手机 | 状态栏 + 挖孔区域 + 导航栏 |
| 折叠屏 | 展开态和折叠态不同 |
| 平板 | 状态栏 + 导航栏 |
4.2 expandSafeArea API
expandSafeArea 是 ArkUI 提供的最便捷的安全区域扩展 API:
typescript
Column()
.expandSafeArea(
[SafeAreaType.SYSTEM], // 安全区域类型
[SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM] // 扩展方向
)
参数说明:
| 参数 | 类型 | 说明 |
|---|---|---|
type |
SafeAreaType[] |
系统安全区域 (SYSTEM) 或自定义安全区域 (CUSTOM) |
edges |
SafeAreaEdge[] |
顶部 (TOP)、底部 (BOTTOM)、左侧 (START)、右侧 (END) |
4.3 安全区域适配的三种策略
在实际开发中,根据 UI 设计的不同,有三种安全区域适配策略:
策略一:内容避开安全区域(默认行为)
typescript
// 默认情况下,ArkUI 组件会自动避开安全区域
Column() {
// 内容自动在安全区域内排列
}
策略二:内容扩展到安全区域(沉浸式)
typescript
// 背景扩展到安全区域,内容保留在安全区域内
Column() {
// 背景层
Column()
.width('100%').height('100%')
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP])
.linearGradient({...})
// 内容层(手动添加 padding 避开状态栏)
Text('标题').padding({ top: 44 }) // 44vp = 状态栏高度
}
策略三:完全沉浸(内容也延伸到安全区域)
typescript
// 全屏沉浸,内容覆盖整个屏幕
Column()
.width('100%').height('100%')
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])
4.4 使用 getWindowAvoidArea 获取安全区域
如果需要更精确的安全区域数据,可以使用 getWindowAvoidArea:
typescript
import { window } from '@kit.ArkUI'
async function getSafeAreaInsets(win: window.MainWindow) {
const area = await win.getWindowAvoidArea(window.AvoidAreaType.TYPE_SYSTEM)
// area 包含 topRect, bottomRect, leftRect, rightRect
console.info(`顶部安全区域高度: ${area.topRect.height}`)
console.info(`底部安全区域高度: ${area.bottomRect.height}`)
}
AvoidArea 的数据结构:
| 属性 | 类型 | 说明 |
|---|---|---|
topRect |
Rect |
顶部安全区域 |
bottomRect |
Rect |
底部安全区域 |
leftRect |
Rect |
左侧安全区域 |
rightRect |
Rect |
右侧安全区域 |
五、窗口属性配置
5.1 窗口尺寸与位置
MainWindow 提供了获取和设置窗口尺寸的 API:
typescript
// 获取窗口属性
const props = win.getWindowProperties()
const rect = props.windowRect
// 获取窗口尺寸
console.info(`窗口尺寸: ${rect.width} x ${rect.height}`)
console.info(`窗口位置: (${rect.left}, ${rect.top})`)
// 设置窗口尺寸(仅对支持自由窗口的设备有效)
win.setWindowSize(390, 844)
5.2 窗口背景色
设置窗口背景色可以避免页面加载时的白屏闪烁:
typescript
win.setWindowBackgroundColor('#FAFAF7')
"海风日记"的页面背景色是暖奶白色(#FAFAF7),设置窗口背景色与此一致,可以保证:
- 应用启动时背景色与首屏页面一致
- 页面切换时不会出现颜色跳跃
- 深色模式切换时背景色平滑过渡
5.3 窗口其他配置
typescript
// 设置窗口是否可调整大小
win.setWindowResizable(false)
// 设置窗口是否全屏
win.setWindowLayoutFullScreen(true)
// 设置窗口是否可触摸
win.setWindowTouchable(true)
// 设置窗口是否保持唤醒
win.setWindowKeepScreenOn(true)
// 设置窗口的亮度模式
win.setWindowBrightness(1.0)
5.4 窗口事件监听
监听窗口变化事件,可以动态调整布局:
typescript
// 监听窗口尺寸变化
const listener = win.on('windowSizeChange', (size: window.Size) => {
console.info(`窗口尺寸变化: ${size.width} x ${size.height}`)
// 根据新的尺寸调整布局
})
// 监听窗口焦点变化
win.on('windowEvent', (event: window.WindowEvent) => {
if (event === window.WindowEventType.WINDOW_ACTIVE) {
console.info('窗口获得焦点')
} else if (event === window.WindowEventType.WINDOW_INACTIVE) {
console.info('窗口失去焦点')
}
})
六、多窗口与折叠屏适配
6.1 分屏模式
HarmonyOS 支持分屏模式,应用需要适配不同的窗口尺寸:
typescript
// 在分屏模式下,窗口尺寸会变小
// 需要监听窗口尺寸变化并调整布局
win.on('windowSizeChange', (size) => {
if (size.width < 600) {
// 紧凑布局:单栏显示
this.useCompactLayout()
} else {
// 宽屏布局:双栏显示
this.useExpandedLayout()
}
})
6.2 折叠屏适配
折叠屏设备在展开和折叠时,窗口尺寸会发生变化:
| 状态 | 窗口特征 | 适配策略 |
|---|---|---|
| 折叠态 | 手机尺寸(约 390×844) | 使用手机布局 |
| 半展开态 | 正方形或接近方形 | 需要自适应布局 |
| 展开态 | 平板尺寸(约 904×1200) | 使用平板布局 |
typescript
// 折叠屏适配的最佳实践
function getLayoutMode(width: number): string {
if (width < 600) return 'compact' // 手机/折叠态
if (width < 840) return 'medium' // 半展开态
return 'expanded' // 平板/展开态
}
6.3 多窗口启动
HarmonyOS 支持多窗口(多实例)启动,需要在 module.json5 中配置:
json5
{
"abilities": [
{
"name": "EntryAbility",
"launchType": "multiton" // 多实例模式
}
]
}
launchType 支持三种模式:
| 模式 | 说明 | 适用场景 |
|---|---|---|
singleton |
单实例,默认模式 | 大多数应用 |
standard |
每次启动创建新实例 | 编辑类应用 |
multiton |
多实例,有限制 | 文件查看器 |
七、窗口管理的最佳实践
7.1 从入口到页面的完整流程
"海风日记"从启动到首屏显示的完整流程:
应用启动
↓
EntryAbility.onCreate()
↓
EntryAbility.onWindowStageCreate()
↓
├── windowStage.loadContent('pages/SplashPage')
│ ↓
│ SplashPage 组件初始化
│ ↓
│ Canvas 绘制波浪动画
│ ↓
│ 用户点击"登录"或"跳过"
│ ↓
└── router.replaceUrl() → 跳转首页
7.2 窗口配置的优先级
窗口配置的优先级规则:
- EntryAbility 中的配置(全局生效)
- 页面组件中的配置(覆盖全局配置)
- 运行时动态配置(优先级最高)
typescript
// 在页面中动态覆盖状态栏配置
aboutToAppear() {
window.getLastWindow(getContext()).then((win) => {
win.setWindowSystemBarProperties({
statusBarContentColor: '#FFFFFF' // 深色页面使用白色文字
})
})
}
7.3 性能优化建议
- 延迟加载非首屏资源 :在
onWindowStageCreate中只加载首屏页面 - 避免频繁配置窗口:窗口属性变化会触发重绘,应合并配置
- 使用动画过渡 :状态栏颜色变化使用
enableStatusBarAnimation平滑过渡 - 监听窗口变化:及时响应窗口尺寸变化,调整布局
八、常见问题与排查
8.1 状态栏变黑
问题:设置了透明状态栏,但状态栏显示为黑色。
原因 :页面没有使用 expandSafeArea 扩展安全区域,或者窗口背景色未设置。
解决方案:
typescript
// 1. 在 EntryAbility 中设置窗口背景色
win.setWindowBackgroundColor('#FAFAF7')
// 2. 在页面中扩展安全区域
Column()
.expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP])
8.2 内容被状态栏遮挡
问题:页面顶部内容被状态栏遮挡。
原因 :使用了 expandSafeArea 但没有给内容添加 padding。
解决方案:
typescript
Column() {
// 顶部导航栏手动添加 padding
Row()
.height(44)
.padding({ top: 44 }) // 状态栏高度
}
8.3 窗口尺寸获取为 0
问题:在 onCreate 中获取窗口尺寸返回 0。
原因:窗口尚未创建完成。
解决方案 :在 onWindowStageCreate 或 onForeground 中获取窗口信息。
typescript
onWindowStageCreate(windowStage: window.WindowStage): void {
// 窗口已创建,此时可以获取窗口尺寸
windowStage.getMainWindow().then((win) => {
const rect = win.getWindowProperties().windowRect
console.info(`窗口尺寸: ${rect.width} x ${rect.height}`)
})
}
总结
本文详细讲解了 HarmonyOS 中 WindowStage 窗口管理的完整体系,包括:
- 透明状态栏 :通过
setWindowSystemBarProperties实现状态栏透明化和颜色适配 - 沉浸式布局 :通过
expandSafeArea和setWindowLayoutFullScreen实现全屏沉浸效果 - 安全区域适配:理解 SafeArea 的概念,掌握三种适配策略
- 窗口属性配置:窗口尺寸、背景色、事件监听等完整 API
- 多窗口与折叠屏适配:响应式布局的最佳实践
掌握窗口管理,是打造专业级鸿蒙应用 的必备技能。下一篇文章将深入讲解 Tabs 组件深度定制,包括 5 Tab 导航栏的设计实现和中间浮动按钮的巧妙布局,敬请期待。
如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!
相关资源: