在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

一、前置思考

1.1 大屏多窗口:从"单应用"到"窗口编排"

平板上办公的核心是同时处理多件事:视频会议 + 记笔记 + 查资料 + 看邮件。如果应用只能全屏单窗口,用户被迫频繁切换——效率断崖。

多窗口能力不足的表现:

❌ 窗口不能拖拽缩放 → 布局固定,无法自定义工作区
❌ 分屏后布局错乱 → 左右 50% 显示不下
❌ 视频不能画中画 → 看视频就不能干别的
❌ 多实例不可用 → 想开两个文档窗口没门

1.2 大屏多窗口的四大能力

① 窗口拖拽/缩放: 用户自由调整窗口大小位置
② 分屏联动: 双窗口并行 + 状态联动(邮件+日历)
③ 焦点管控: 多窗口间的输入焦点/语音焦点管理
④ 画中画 + 多实例: 视频悬浮 + 同应用多窗口

1.3 本文价值

覆盖窗口管理(拖拽/缩放/分屏)、焦点管控(输入/音频)、画中画(PiP)、多实例(Multi-Window)的完整实现与高阶策略。

二、核心原理

2.1 大屏窗口体系

┌────────────────────────────────────────────┐
│           大屏桌面(自由窗口)               │
│  ┌─────────────┐  ┌─────────────┐         │
│  │ 窗口A(邮件)  │  │ 窗口B(日历)  │         │
│  │ 可拖拽/缩放  │  │ 分屏联动     │         │
│  └─────────────┘  └─────────────┘         │
│  ┌─────────────────────────────────┐      │
│  │ 画中画 (视频悬浮)                 │      │
│  └─────────────────────────────────┘      │
└────────────────────────────────────────────┘

窗口模式:
  全屏 (Fullscreen)     → 单应用独占
  分屏 (Split)          → 左右 50%/可调
  悬浮 (Floating)       → 自由拖拽小窗
  画中画 (PiP)          → 视频悬浮窗

2.2 焦点管控机制

多窗口下的焦点类型:
① 输入焦点: 键盘/触摸输入指向哪个窗口
② 音频焦点: 声音输出到哪个窗口(音乐+会议冲突)
③ 视觉焦点: 哪个窗口高亮(当前操作)

焦点切换规则:
  点击窗口 → 输入焦点转移 + 视觉高亮
  音频焦点: 遵循优先级(电话 > 会议 > 音乐)

2.3 多实例管理

多实例 = 同一应用同时打开多个窗口

场景:
  开两个文档窗口对比编辑
  开两个聊天窗口分别处理
  开多个浏览器窗口

实例管理:
  每个窗口独立生命周期
  实例间数据隔离或共享(按需)
  窗口销毁只影响自身实例

三、源码/API 深度解析

3.1 窗口创建与模式切换

import { window } from '@kit.ArkUI';

// 1. 获取当前窗口
async function getMainWindow(): Promise<window.Window> {
  const context = getContext(this) as common.UIAbilityContext;
  return await window.getLastWindow(context);
}

// 2. 支持分屏/悬浮模式(应用声明)
// module.json5:
// "supportWindowMode": ["fullscreen", "split", "floating"]

// 3. 监听窗口尺寸变化(分屏时自动调整布局)
function watchWindowSize(): void {
  const win = window.getLastWindow(getContext(this));
  win.then((w: window.Window) => {
    w.on('windowSizeChange', (size: window.Size) => {
      if (size.width < 600) {
        // 分屏压缩 → 紧凑布局
        LayoutManager.switchToCompact();
      } else {
        LayoutManager.switchToWide();
      }
    });
  });
}

3.2 分屏联动(状态同步)

import { distributedDataObject } from '@kit.ArkData';

// 分屏的两个窗口共享状态(邮件 + 日历联动)
// 方案: 使用分布式数据对象或 AppStorage 共享

// 场景: 邮件窗口点击日期 → 日历窗口跳转到该日期
class SplitLinkDemo {
  private shared: distributedDataObject.DataObject;

  init(): void {
    const context = getContext(this) as common.UIAbilityContext;
    const options = { sessionId: 'MAIL_CAL_LINK' };
    this.shared = distributedDataObject.create(context, options);

    // 订阅另一端变化
    this.shared.on('change', (sessionId, data) => {
      if (data.attributes.includes('date')) {
        CalendarPage.jumpTo(this.shared['date']);
      }
    });
  }

  // 点击邮件中的日期
  onMailDateClick(date: string): void {
    this.shared['date'] = date;  // 自动同步到日历窗口
  }
}

3.3 画中画(PiP)

import { window } from '@kit.ArkUI';

// 1. 创建画中画控制器
const context = getContext(this) as common.UIAbilityContext;
const controller = window.getPiPController(context);

// 2. 开启画中画(视频切到悬浮窗)
async function enterPip(): Promise<void> {
  const pipOptions: window.PiPWindowOptions = {
    context: context,
    componentController: videoController,   // 视频组件控制器
    templateType: window.PiPTemplateType.VIDEO_PLAY,
    contentWidth: 320,
    contentHeight: 180
  };
  await controller.startPiP(pipOptions);
  LoggerUtil.info(TAG, '已进入画中画模式');
}

// 3. 监听画中画生命周期
controller.on('pipWindowStateChange', (state: window.PiPWindowState) => {
  // RESTORED / FULL_SCREEN / CLOSED
  if (state === window.PiPWindowState.RESTORED) {
    this.resumeFullUI();
  }
});

3.4 多实例管理

import { common, UIAbility } from '@kit.AbilityKit';

// 1. 应用声明支持多实例
// module.json5:
// "launchType": "singleton",  // 或 "standard" (每开一个窗口一个实例)

// 2. 创建新实例窗口(打开第二个文档窗口)
async function openNewDocumentWindow(): Promise<void> {
  const context = getContext(this) as common.UIAbilityContext;
  const want: Want = {
    bundleName: 'com.example.docs',
    abilityName: 'MainAbility',
    parameters: { docId: 'doc_2' }
  };
  // 以多实例方式启动
  await context.startAbility(want, {
    windowMode: window.WindowMode.FLOATING  // 悬浮窗口打开
  });
}

// 3. 实例间通信(可选: 共享数据)
// 通过 distributedDataObject 或 EventHub 传递

四、企业级实战落地

4.1 多窗口开发清单

能力 落地动作 验收
窗口适配 分屏/悬浮尺寸自适应 300~2000vp 均正常
分屏联动 共享状态同步 双向联动无延迟
画中画 视频悬浮播放 切换/恢复正常
多实例 文档多窗口 实例独立生命周期
焦点管控 输入/音频焦点策略 无焦点冲突

4.2 完整示例:会议+笔记分屏协作

@Entry
@ComponentV2
struct MultiWindowDemo {
  @Local windowMode: string = '全屏';
  @Local focusTarget: string = '会议窗口';
  @Local pipActive: boolean = false;

  // 模拟分屏联动
  private toggleSplit(): void {
    this.windowMode = this.windowMode === '全屏' ? '分屏 50%' : '全屏';
    this.log('📐 窗口模式: ' + this.windowMode);
    this.log(this.windowMode === '分屏 50%'
      ? '📋 会议窗口 + 笔记窗口 并行'
      : '🖥️ 恢复全屏显示');
  }

  // 模拟画中画
  private togglePip(): void {
    this.pipActive = !this.pipActive;
    this.log(this.pipActive
      ? '🎬 视频进入画中画: 悬浮窗口 320x180'
      : '🎬 视频恢复全屏');
  }

  // 模拟焦点切换
  private switchFocus(): void {
    this.focusTarget = this.focusTarget === '会议窗口' ? '笔记窗口' : '会议窗口';
    this.log('🎯 焦点切换到: ' + this.focusTarget);
  }

  private log(msg: string): void {
    this.logs.push(msg);
  }

  @Local logs: string[] = [];

  build() {
    Column({ space: 12 }) {
      Text('🪟 大屏多窗口开发').fontSize(20).fontWeight(FontWeight.Bold)
      Text('窗口模式: ' + this.windowMode).fontSize(14).fontColor('#4FC3F7')
      Text('焦点: ' + this.focusTarget).fontSize(14).fontColor('#FFD54F')

      Row({ space: 8 }) {
        Button('分屏切换').layoutWeight(1).height(42).fontSize(12)
          .onClick(() => this.toggleSplit())
        Button('画中画').layoutWeight(1).height(42).fontSize(12)
          .onClick(() => this.togglePip())
        Button('焦点切换').layoutWeight(1).height(42).fontSize(12)
          .onClick(() => this.switchFocus())
      }
      .width('100%')

      // 分屏模拟视图
      if (this.windowMode.startsWith('分屏')) {
        Row() {
          Column() {
            Text('📹 会议窗口').fontSize(13).fontWeight(FontWeight.Bold)
            Text('视频画面').fontSize(11).fontColor('rgba(255,255,255,0.4)')
          }.layoutWeight(1).height(120).padding(10)
            .backgroundColor('rgba(79,195,247,0.1)').borderRadius(8)

          Column() {
            Text('📝 笔记窗口').fontSize(13).fontWeight(FontWeight.Bold)
            Text('会议纪要…').fontSize(11).fontColor('rgba(255,255,255,0.4)')
          }.layoutWeight(1).height(120).padding(10)
            .backgroundColor('rgba(255,213,79,0.08)').borderRadius(8)
        }
        .width('100%')
      }

      ForEach(this.logs, (l: string) => {
        Text(l).fontSize(11).fontColor('rgba(255,255,255,0.7)').width('100%')
      }, (l: string, i: number) => l + i)
    }
    .width('100%').height('100%').padding(16)
    .backgroundColor('#0D1B2A')
  }
}

4.3 多窗口场景矩阵

场景 窗口模式 联动 焦点
会议+笔记 分屏 50% 会议内容同步笔记 笔记输入
视频+聊天 画中画+悬浮 播放进度共享 聊天输入
文档对比 双实例并排 实例数据隔离 当前编辑
邮件+日历 分屏可调 日期双向联动 点击即切

五、问题排查与性能优化

问题 原因 解决
分屏后布局崩 未监听尺寸变化 SizeChange 动态布局
画中画黑屏 未保留视频状态 组件状态迁移
多窗口内存高 每实例独立数据 共享数据 + 实例复用
焦点混乱 无焦点策略 输入/音频焦点分级
分屏联动失效 状态未共享 分布式数据对象
窗口拖拽卡顿 布局重算频繁 拖拽结束才重排

5.1 多窗口性能优化

1. 分屏后立刻释放非可见资源(离屏渲染缓存)
2. 多实例共享只读数据(配置/资源),隔离可写数据
3. 画中画降帧: 悬浮窗降为 30fps 省电
4. 窗口拖拽用 transform 跟随,松手才触发布局重算
5. 焦点切换时预加载目标窗口数据

六、高阶总结与最佳实践

  1. 窗口即容器:布局设计围绕"任意窗口尺寸都能自适应"展开。
  2. 联动靠共享:分屏联动的本质是状态共享,用分布式数据对象打通。
  3. 焦点要分级:输入焦点和音频焦点分开管理,电话优先、音乐让位。
  4. 实例隔离有道:多实例该共享的共享(配置)、该隔离的隔离(数据)。
  5. 画中画是刚需:视频类应用不提供画中画,大屏体验直接减半。

一句话记住:大屏多窗口 = 窗口自由(拖拽分屏悬浮)+ 状态联动(共享数据)+ 焦点有序(输入音频分级)+ 实例可管(共享隔离兼顾)——让平板真正成为多任务生产力平台。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐