WindowStage 多窗口与沉浸式窗口实践:从适配到体验细节

WindowStage 多窗口与沉浸式窗口实践:从适配到体验细节

窗口体验不是简单地"铺满屏幕"。横竖屏、折叠屏、平板分屏、沉浸式状态栏、弹窗窗口都会影响用户感受。Stage 模型下,窗口管理集中在 WindowStage 和 Window 对象上,工程代码要把布局适配和业务页面解耦。

本文用一个内容阅读类应用做例子:手机全屏阅读、平板左右分栏、沉浸式图片预览、弹窗确认,都通过统一的窗口策略管理。

1. 先建立窗口场景模型

窗口适配不要直接写一堆 if width > xxx。先把窗口场景抽象出来,页面只关心当前是紧凑、普通还是宽屏。

ts 复制代码
export enum WindowSizeClass {
  Compact = 'compact',
  Medium = 'medium',
  Expanded = 'expanded'
}

export function resolveSizeClass(widthVp: number): WindowSizeClass {
  if (widthVp < 600) {
    return WindowSizeClass.Compact;
  }
  if (widthVp < 840) {
    return WindowSizeClass.Medium;
  }
  return WindowSizeClass.Expanded;
}

代码解释:

  1. 阈值集中在一个函数里,方便后续跟设计规范同步。
  2. 页面只使用 WindowSizeClass,不直接依赖具体像素。
  3. 平板、折叠屏、分屏都可以复用这套判断。

2. 监听窗口尺寸变化

用户拖动分屏或旋转设备时,窗口尺寸会变化。页面应响应尺寸类变化,而不是只在启动时判断一次。

ts 复制代码
import { window } from '@kit.ArkUI';

export class WindowSizeStore {
  private static sizeClass: WindowSizeClass = WindowSizeClass.Compact;

  static update(mainWindow: window.Window): void {
    const rect = mainWindow.getWindowProperties().windowRect;
    WindowSizeStore.sizeClass = resolveSizeClass(rect.width);
  }

  static current(): WindowSizeClass {
    return WindowSizeStore.sizeClass;
  }
}

代码解释:

  1. 从窗口属性读取宽度,统一转换为尺寸类。
  2. 页面通过 store 读取当前状态,避免各自访问窗口对象。
  3. 如果项目使用状态管理,可以把这里改成可观察状态。

3. 沉浸式状态栏要按页面设置

不是所有页面都适合沉浸式。图片预览页需要沉浸,表单页可能不需要。建议把沉浸式配置做成页面策略。

ts 复制代码
export interface ImmersiveConfig {
  enabled: boolean;
  statusBarColor: string;
  navigationBarColor: string;
  lightIcon: boolean;
}

export const immersiveRules: Record<string, ImmersiveConfig> = {
  Home: { enabled: false, statusBarColor: '#FFFFFF', navigationBarColor: '#FFFFFF', lightIcon: false },
  Reader: { enabled: true, statusBarColor: '#00000000', navigationBarColor: '#00000000', lightIcon: true },
  ImagePreview: { enabled: true, statusBarColor: '#00000000', navigationBarColor: '#000000', lightIcon: true }
};

代码解释:

  1. 沉浸式按页面配置,避免全局一刀切。
  2. 透明状态栏适合图片、阅读等内容型页面。
  3. 图标明暗要和背景搭配,否则状态栏会看不清。

4. 应用沉浸式配置

获取主窗口后,根据当前页面应用窗口属性。实际项目中这段可以放在页面进入时调用。

ts 复制代码
export class ImmersiveWindowService {
  static async apply(pageName: string, mainWindow: window.Window): Promise<void> {
    const config = immersiveRules[pageName] ?? immersiveRules.Home;
    await mainWindow.setWindowLayoutFullScreen(config.enabled);
    await mainWindow.setWindowSystemBarProperties({
      statusBarColor: config.statusBarColor,
      navigationBarColor: config.navigationBarColor,
      statusBarContentColor: config.lightIcon ? '#FFFFFF' : '#111111',
      navigationBarContentColor: config.lightIcon ? '#FFFFFF' : '#111111'
    });
  }
}

代码解释:

  1. setWindowLayoutFullScreen 控制内容是否延伸到系统栏区域。
  2. 系统栏颜色和图标颜色要成组设置。
  3. 没有配置的页面回到 Home 规则,避免继承上一个页面的沉浸式状态。

5. 宽屏页面不要只是拉伸

平板或横屏下,如果只是把手机页面拉宽,阅读体验会变差。宽屏场景应该使用分栏或居中最大宽度。

ts 复制代码
export function resolveReaderLayout(sizeClass: WindowSizeClass): string {
  switch (sizeClass) {
    case WindowSizeClass.Expanded:
      return 'twoColumn';
    case WindowSizeClass.Medium:
      return 'centerMaxWidth';
    default:
      return 'singleColumn';
  }
}

代码解释:

  1. 宽屏采用双栏,利用横向空间。
  2. 中等宽度居中限制最大宽度,避免行长过长。
  3. 手机保持单栏,减少操作复杂度。

6. 弹窗窗口要控制尺寸和焦点

确认弹窗、筛选面板、分享面板都不应该直接占满全屏。窗口策略可以统一控制弹层尺寸、圆角和焦点行为。

ts 复制代码
export interface DialogWindowOptions {
  widthVp: number;
  heightVp: number;
  focusable: boolean;
}

export function resolveDialogOptions(sizeClass: WindowSizeClass): DialogWindowOptions {
  if (sizeClass === WindowSizeClass.Expanded) {
    return { widthVp: 520, heightVp: 420, focusable: true };
  }
  return { widthVp: 360, heightVp: 420, focusable: true };
}

代码解释:

  1. 弹窗尺寸随窗口场景变化,不在组件里写死。
  2. focusable 明确弹窗是否接收焦点。
  3. 平板弹窗可以更宽,但仍要限制最大尺寸。

7. 窗口状态恢复要有默认值

窗口策略依赖运行时状态,但页面重建时状态可能为空。每个窗口配置都要有默认值。

ts 复制代码
export class WindowProfile {
  constructor(
    readonly sizeClass: WindowSizeClass = WindowSizeClass.Compact,
    readonly immersive: ImmersiveConfig = immersiveRules.Home
  ) {}

  static fallback(): WindowProfile {
    return new WindowProfile();
  }
}

代码解释:

  1. 默认紧凑布局保证手机场景优先可用。
  2. 默认非沉浸式,避免系统栏文字不可见。
  3. 恢复失败时使用 fallback,页面至少能正常展示。

8. 调试窗口问题要记录尺寸和页面

窗口适配问题经常只在某些设备出现。日志里必须有页面名、窗口宽高、尺寸类、沉浸式状态。

ts 复制代码
export function logWindowProfile(page: string, width: number, height: number): void {
  const sizeClass = resolveSizeClass(width);
  hilog.info(
    0x20260702,
    'WindowProfile',
    'page=%{public}s width=%{public}d height=%{public}d class=%{public}s',
    page,
    width,
    height,
    sizeClass
  );
}

代码解释:

  1. 没有宽高日志,适配问题很难远程复现。
  2. 记录尺寸类能验证策略是否命中。
  3. 页面名用于判断是不是某个页面漏应用窗口规则。

9. 验收清单

场景 检查点
手机竖屏 单栏、状态栏清晰
手机横屏 内容不被系统栏遮挡
平板分屏 尺寸变化后布局刷新
图片预览 沉浸式生效,返回后恢复
弹窗 宽屏不拉满,焦点正确

10. 小结

多窗口和沉浸式体验的关键是"页面不直接管理窗口细节"。把尺寸类、沉浸式规则、弹窗尺寸和日志统一到窗口服务里,业务页面只根据策略渲染内容。这样应用从手机扩展到平板、折叠屏和分屏时,不需要重写一套页面。

相关推荐
小羊没烦恼!2 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
LaughingZhu2 天前
Product Hunt 每日热榜 | 2026-09-19
人工智能·深度学习·神经网络·搜索引擎·百度
三十而立洋2 天前
Cookie 详解:从产生到安全,一次讲透
前端·javascript
youyin2 天前
HarmonyOS ArkUI 组件与自定义组件零基础:从搭页面到组件化开发
华为·harmonyos
伞伞悦读2 天前
【第38期】Python 模块与包详解:import、from、模块搜索路径、包结构和 __init__
开发语言·python
猎头南楼3 天前
知识社区推荐系统实践:新用户冷启动与长短期兴趣建模的挑战 资深推荐算法工程师
人工智能·深度学习·算法·机器学习
卡布鲁3 天前
把一个 Vite + Vue3 应用塞进 qiankun (React + Umi3) 主站:十个坑的复盘
前端·javascript·react.js
高洁013 天前
未来三年,AI 落地的五个确定性判断一
人工智能·深度学习·机器学习·transformer·知识图谱
C语言小火车3 天前
C/C++ 为什么需要编译器?
开发语言·c++
李少兄3 天前
JavaScript 隐式全局变量解析
javascript