UIDesignKit HdsNavigation:折叠屏多窗入口与草稿隔离【鸿蒙心迹】
同一条巡检工单,在手机上往往是一张页面;换成展开态折叠屏,用户会希望把资料窗口与填写窗口同时放在眼前。需求听起来只是“再开一扇窗”,真正容易出问题的却是草稿归属:左边修改了半句记录,右边的新窗口不能凭一次重建就把这半句当成已提交内容。另一个误区是把所有折叠设备都视作天然支持应用内多窗。系统能力有明确边界,业务状态也需要单独设计。
本篇以 SplitDesk 工单复核演示为对象,固定记录编号 SD-028,工作窗标记 A,当前草稿为“灯光区域待复核”,状态为“草稿未提交”。它不是某台设备已经通过的运行报告,而是一套可用于接入验证的工程骨架。

一、先划清“多窗入口”和“宽屏双栏”
ArkUI 页面在同一个窗口内使用 Row、Grid 或 Navigation 显示左右分栏,是布局问题;借助应用内多窗入口打开同一应用的另一个 UIAbility,则涉及窗口、Ability 实例以及任务管理。两者都能呈现“两个区域”,但生命周期不同。不能拿宽屏预览中出现了两列,反推第二个应用窗口一定创建成功。
华为 2026 年 9 月更新的应用内多窗指导说明,HdsNavigation 的标题栏菜单可设置 multiWindowEntryInAPPMenu。这项能力从 6.0.0(20) 起提供,要求当前设备和屏幕状态支持全景多窗。官方列出的典型形态包括双折叠展开态、三折叠规定的双屏或三屏横屏态,以及平板横屏态;不支持的形态中入口不会响应点击。这里的 20 是文档标示的能力起始版本,不等于本文声称已在某个 SDK 或设备上完成验证。
因此产品页可以一直显示工单内容,却不应把“入口一定能用”写成保证。主窗口的编辑和保存必须保持完整功能;入口可用时才提供多窗体验。不应通过擅自模拟系统多窗图标来误导用户。
二、用系统菜单表达能力,不在业务按钮里伪造开窗
此处的代码解决“标题栏向系统声明哪一个应用内 UIAbility 是新窗口目标”的问题。Demo 假定工程已实际注册 ReviewAbility,且包名为 com.example.splitdesk;如果工程命名不同,必须同时修改 Want 与 Ability 配置。代码不负责修改设备的多窗支持状态。
import { HdsNavigation, HdsNavigationMenuContentOptions,
HdsNavigationAttribute } from '@kit.UIDesignKit';
import { Want } from '@kit.AbilityKit';
@Entry
@Component
struct SplitDeskHome {
private targetWant: Want = {
bundleName: 'com.example.splitdesk',
moduleName: 'entry',
abilityName: 'ReviewAbility'
};
@State menuContent: HdsNavigationMenuContentOptions = {
multiWindowEntryInAPPMenu: { want: this.targetWant },
maxCount: 1,
value: []
};
build() {
HdsNavigation() {
Text('工单 SD-028 · 请在本窗保存草稿')
.fontSize(18).padding(20)
}
.titleBar({
content: {
title: { mainTitle: 'SplitDesk · 工作窗 A' },
menu: this.menuContent
}
})
}
}
targetWant 的三项身份字段必须都指向当前应用,不能被理解成跨应用唤起。maxCount 是菜单中图标显示数量的设置,不是系统允许打开的窗口数量。@State menuContent 服务于组件属性刷新,但系统实际是否执行多窗仍受设备能力限制。更不能把用户点击菜单的意图,当成新窗口已经成功加载页面的证据。
实际工程还需核对 UIAbility 的声明、路由与生命周期回调,必要时在目标 Ability 打印创建、前台、后台和销毁事件。这里不伪造这些事件的真实日志。
三、草稿跟着记录走,但不自动跨窗口传播
工单字段与窗口实例不应共用一个含义含糊的 currentText。对于 SD-028,最少需要区分记录 ID、编辑版本、当前草稿和是否有未保存修改。用户在 A 窗修改“灯光区域待复核”时,B 窗可以显示上一次已保存的版本,而不是毫无提示地接收 A 窗尚未提交的文本。如果要做跨窗实时协同,应另外设计同步协议、冲突提示与存储层,不能依赖 @State 自然同步。
下面是与 UI 解耦的状态模型,解决在同一窗口内把“输入发生变化”和“真正保存成功”区分开来的问题。revision 是本地业务计数,既不是数据库事务 ID,也不能当服务端版本号。
export class ReviewDraft {
readonly recordId: string;
text: string;
revision: number = 0;
dirty: boolean = false;
constructor(recordId: string, initialText: string) {
this.recordId = recordId;
this.text = initialText;
}
edit(nextText: string): void {
if (nextText === this.text) return;
this.text = nextText;
this.revision += 1;
this.dirty = true;
}
markSaved(savedRevision: number): void {
if (savedRevision === this.revision) this.dirty = false;
}
}
保存动作必须在持久化或服务端确认之后调用 markSaved,否则按钮点击一次就会被误记成保存成功。异步保存期间用户可能继续输入;旧回调的版本小于当前版本时,不应该清空 dirty。退出窗口时若还有未保存文本,应用应按产品规则询问是否放弃或先保存,而非直接覆盖其他窗口的记录。
四、让演示 UI 只描述已知状态
页面上可同时出现“工作窗 A”“SD-028”和“草稿未提交”,这些标识帮助测试人员确认自己究竟在看哪个状态。下面的 ArkTS 页面片段仅模拟本地编辑,不接数据库,也不承诺已经开启了第二个窗口。它用于检查输入更新与未提交提示是否同步。
@Entry
@Component
struct ReviewDesk {
@State private draft: string = '灯光区域待复核';
@State private dirty: boolean = true;
aboutToAppear(): void {
console.info('SplitDesk window=A record=SD-028 draft=dirty');
}
build() {
Column({ space: 16 }) {
Text('SD-028 · 展厅巡检记录').fontSize(22)
Text(this.dirty ? '草稿未提交' : '本地已确认')
.fontColor(this.dirty ? Color.Orange : Color.Green)
TextInput({ text: this.draft, placeholder: '填写巡检备注' })
.onChange((value: string) => {
this.draft = value;
this.dirty = true;
})
Button('演示:确认本地状态')
.onClick(() => { this.dirty = false; })
}.padding(20).width('100%')
}
}
onChange 只写当前组件持有的状态,避免在输入瞬间产生跨窗副作用。按钮名称故意写明“演示”,因为它没有完成网络或数据库持久化;真实工程不可直接沿用这种伪保存行为。aboutToAppear 的日志只表示该页面进入显示阶段,不能证明系统多窗创建成功。

五、围绕一条工单准备对照检查
建议从普通直板屏开始做负向检查:主窗编辑 SD-028 是否顺畅,系统多窗入口不可交互时业务操作是否依然存在。然后在符合条件的折叠展开态或平板横屏态检查系统入口;若可以创建第二个窗口,再观察两个 UIAbility 的创建、显示、后台与退出次序。最后返回 A 窗,确认未保存草稿没有因另一窗口载入而改变。
本篇示意图固定了 工作窗 A、记录 SD-028、草稿“灯光区域待复核”、状态“草稿未提交”。DevEco 演示画面底部的 SplitDesk window=A record=SD-028 draft=dirty 是与上面示例对应的预期日志文本,不是已采集的设备日志。图中的“入口需设备支持”也只是能力边界提示,不能代替设备兼容性检测。
手机演示图采取单窗口布局,是为了说明一个容易忽略的原则:即使没有第二个窗口,用户仍能看见草稿状态并完成必要操作。若图示中出现“展开态”标签,其含义仅为测试场景标记,非设备硬件鉴定。

六、跨窗口误同步通常发生在这些边界
第一种情况是把应用级单例当成了“当前编辑记录”,新窗口初始化时写入默认值,旧窗口的草稿随之被覆盖。单例可以保存已提交数据的缓存,却不适合作为无版本控制的多窗编辑器。第二种情况是两个窗口同时提交 SD-028,客户端只保留最后到达的响应;若服务端没有版本条件,先编辑的内容可能被悄悄改写。业务应该使用持久化版本或服务端条件更新,并明确告诉用户发生了冲突。
第三种情况是窗口隐藏就直接释放尚未完成的保存任务。生命周期事件是资源管理信号,不天然等价于业务取消。网络请求能否继续、草稿是否落盘、页面是否弹出确认,都应由明确的产品规则决定。第四种情况是把 HdsNavigation 菜单中的多窗入口当作自定义导航 API;它不会自动为你完成草稿迁移、权限继承或跨实例对象共享。
调试时最好分别记录 windowInstanceId(业务自己生成的诊断标识)、recordId、revision 和 dirty。不要把设备生成的进程 PID 当作窗口唯一 ID,也不要在 HiLog 中输出用户填写的完整敏感正文。像 SD-028 这样的编号是本文合成演示数据,可以公开展示;真实工单应按业务脱敏规则处理。
七、交付前的判断应当克制
这套实现首先解决两件事:调用官方提供的应用内多窗入口配置,以及保证没有第二个窗口时主页面仍然可靠。它没有实现跨窗实时协同,没有保证任何设备一定提供系统多窗,也没有通过 DevEco 编译或真机多形态适配测试。后续真正落地时,应该把已注册的 ReviewAbility、草稿持久化、冲突解决和设备兼容性列入同一张验收表。
对于折叠屏产品,最容易被高估的并不是“能多显示一块区域”,而是那块区域背后的实例和状态边界。界面可以随着设备展开,未保存的工作内容却不能跟着变得含糊。这也是本例最终保留“草稿未提交”提示而不展示虚构成功截图的原因。
资料依据: 华为开发者文档:设置应用内多窗;HdsNavigation API 参考。示意项目和日志为说明性材料,所有代码须按实际工程模块、Ability 注册和设备能力验证。
更多推荐
所有评论(0)