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

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

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

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

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. 小结

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

相关推荐
星空露珠1 小时前
迷你世界UGc3.0脚本Wiki[剧情动画模块管理接口 Timeline]
开发语言·数据结构·算法·游戏·lua
大鱼>1 小时前
深度学习入门:神经网络原理与 PyTorch 实战
pytorch·深度学习·神经网络
手写码匠2 小时前
手写 LLM 安全护栏:从内容审核到越狱防御的完整实现
人工智能·深度学习·算法·aigc
大囚长2 小时前
信息约简对智能系统预测的重要性
人工智能·深度学习·机器学习
未来之窗软件服务2 小时前
计算机考试-C语言 应用题—东方仙盟
c语言·开发语言·仙盟创梦ide·东方仙盟·计算机考试
想你依然心痛2 小时前
AtomCode在后端开发中的实战体验:Go微服务从零搭建
开发语言·微服务·golang
我是一颗柠檬2 小时前
【Java项目技术亮点】EXPLAIN深度分析与慢查询治理
android·java·开发语言
luj_17682 小时前
草酸与烟酸对消化及糖代谢的影响解析
服务器·c语言·开发语言·经验分享·算法
fei_sun2 小时前
【SystemVerilog】SystemVerilog与C语言的接口
c语言·开发语言
W是笔名2 小时前
python___容器类型的数据___序列
开发语言·python