WindowStage 窗口管理:透明状态栏与沉浸式布局

前言

在 HarmonyOS 应用开发中,窗口管理是影响用户体验的关键因素之一。一个优秀的窗口管理方案可以实现沉浸式布局、透明状态栏、适配挖孔屏等效果,让应用看起来更加专业和精致。

本文将以"海风日记"App 为实战案例,深入讲解 WindowStage 窗口管理的完整体系,包括窗口创建、状态栏配置、沉浸式布局、安全区域适配以及多窗口适配等核心内容。通过本文,你将掌握在 HarmonyOS 中实现全沉浸式体验的全部技巧。


一、WindowStage 概述

1.1 什么是 WindowStage

WindowStage 是 HarmonyOS Stage 模型中用于管理应用窗口的类,它负责:

  1. 窗口创建:为 UIAbility 创建和管理主窗口
  2. 页面加载 :通过 loadContent 方法加载首屏页面
  3. 窗口属性配置:设置状态栏、导航栏、窗口尺寸等
  4. 窗口事件监听:监听窗口尺寸变化、焦点变化等

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'  // 暖棕黑文字
  })
})

透明状态栏的实现要点:

  1. statusBarColor 必须使用 8 位色值(含 Alpha 通道),#00000000 表示完全透明
  2. statusBarContentColor 使用 6 位色值,控制时钟、电量等图标的颜色
  3. 透明状态栏需要配合页面布局的 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),设置窗口背景色与此一致,可以保证:

  1. 应用启动时背景色与首屏页面一致
  2. 页面切换时不会出现颜色跳跃
  3. 深色模式切换时背景色平滑过渡

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 窗口配置的优先级

窗口配置的优先级规则:

  1. EntryAbility 中的配置(全局生效)
  2. 页面组件中的配置(覆盖全局配置)
  3. 运行时动态配置(优先级最高)
typescript 复制代码
// 在页面中动态覆盖状态栏配置
aboutToAppear() {
  window.getLastWindow(getContext()).then((win) => {
    win.setWindowSystemBarProperties({
      statusBarContentColor: '#FFFFFF'  // 深色页面使用白色文字
    })
  })
}

7.3 性能优化建议

  1. 延迟加载非首屏资源 :在 onWindowStageCreate 中只加载首屏页面
  2. 避免频繁配置窗口:窗口属性变化会触发重绘,应合并配置
  3. 使用动画过渡 :状态栏颜色变化使用 enableStatusBarAnimation 平滑过渡
  4. 监听窗口变化:及时响应窗口尺寸变化,调整布局

八、常见问题与排查

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。

原因:窗口尚未创建完成。

解决方案 :在 onWindowStageCreateonForeground 中获取窗口信息。

typescript 复制代码
onWindowStageCreate(windowStage: window.WindowStage): void {
  // 窗口已创建,此时可以获取窗口尺寸
  windowStage.getMainWindow().then((win) => {
    const rect = win.getWindowProperties().windowRect
    console.info(`窗口尺寸: ${rect.width} x ${rect.height}`)
  })
}

总结

本文详细讲解了 HarmonyOS 中 WindowStage 窗口管理的完整体系,包括:

  1. 透明状态栏 :通过 setWindowSystemBarProperties 实现状态栏透明化和颜色适配
  2. 沉浸式布局 :通过 expandSafeAreasetWindowLayoutFullScreen 实现全屏沉浸效果
  3. 安全区域适配:理解 SafeArea 的概念,掌握三种适配策略
  4. 窗口属性配置:窗口尺寸、背景色、事件监听等完整 API
  5. 多窗口与折叠屏适配:响应式布局的最佳实践

掌握窗口管理,是打造专业级鸿蒙应用 的必备技能。下一篇文章将深入讲解 Tabs 组件深度定制,包括 5 Tab 导航栏的设计实现和中间浮动按钮的巧妙布局,敬请期待。

如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!


相关资源:

相关推荐
AskHarries6 小时前
Google 登录配置
后端
全堆鸿蒙6 小时前
时间轴组件:Circle 节点 + 竖线连接 + 内容列表
后端
进击的前栈7 小时前
Grid 双栏网格与 List 单栏列表的视图切换实现
后端
xianjixiance_7 小时前
@Entry 与 @Component 装饰器:组件声明、导出与页面入口
后端
w139548564228 小时前
Shadow 阴影体系:多层级阴影提升视觉层次
后端
进击的前栈8 小时前
情绪分布图表:Stack 进度条 + 百分比计算
后端
IT_陈寒8 小时前
JavaScript的异步地狱里,我的Promise掉进了微任务陷阱
前端·人工智能·后端
xianjixiance_8 小时前
Swiper 轮播组件实现 3 页新用户引导
后端
CaffeinePro8 小时前
FastAPI高并发实战:缓存、分布式限流、链路日志三位一体架构
后端·fastapi