智绘鸿蒙 · 如 7 而至端侧 AI 能力实战手记

左自选右行情的单应用多任务

智绘鸿蒙·如 7 而至

前言

平行视界的官方定义只有一句话:未适配分栏的应用,靠一份配置在宽屏上分成两栏。

但我要的不是"能分",是分完之后每一栏到底有多少像素。因为行情页的指标栅格、K 线图的最小可辨宽度、列表行高,全都是按 vp 写死的;分栏比例一变,右栏就从"够用"掉到"挤变形"。文档里 ratio 只写了"左右页面比例",没给实测宽度,也没说越界会怎样。

所以我把它量了一遍。下面所有数字都是同一台 MatePad Pro(HarmonyOS 7 / API 26)上抓的,误差来源只有取整。

基线:这台机器的坐标系

怎么来的
屏幕像素 2800 × 1840 ui layout 根节点 [0,0,2800,1840]
密度 2.125 像素 / vp 反推
屏幕 vp 1318 × 866 2800/2.125、1840/2.125
宽/高 1.522 > 1.2,命中 wideWindowMode 的"长方形宽屏"判定

wideWindowMode 的触发条件是"窗口宽度 ≥ 600vp 且 宽/高 > 1.2"。这台机器横向 1318vp、比值 1.522,两个条件都满足且余量很大。反过来说,竖屏时宽/高 = 0.657,永远进不了这个分支——这就是平行视界只在横屏分栏的数学原因,不是玄学。

配置字段的取值域

easy_go.json 里我关心的四个字段,取值域直接抄 SDK 自带的 JSON Schema(hms/toolchains/modulecheck/easy_go.json),比文档更精确:

字段 取值域 默认 备注
ratio 字符串,正则 ^[1-9]\d* | [1-9]\d*$ 1:1 文档规定范围 1:2 ~ 2:1,越界取边界值
isDraggable boolean false Schema 里 if isDraggable===true then not required ratio——两者互斥,同时写会构建失败
mode 0 或 1 1 0=购物模式(右推左),1=导航模式(左固定)
splitDividerColor {light, dark} 8 位十六进制 6 位过不了校验

还有一条容易忽略的:deviceConfigdisplayModeOptionssettingDisplayOptions 互斥,同一个设备类型节点里只能出现一个。

这个 demo 用的是导航模式:

{
  "common": {
    "displayModeOptions": {
      "wideWindowMode": "routerSplit",
      "routerSplitOptions": {
        "homePage": "pages/Index",
        "relatedPage": "pages/StockDetail",
        "mode": 1,
        "wideSplit": { "ratio": "1 | 2" },
        "splitDividerColor": { "light": "#FF3B6BFF", "dark": "#FF7A9BFF" },
        "supportLandscapeFullscreen": false,
        "enableInSplitScreen": true
      }
    }
  }
}

routerSplit 表示路由由 Router 模块实现;如果页面用的是 Navigation 组件,这里要换成 navigationSplit,两套 options 的字段名一样但 Schema 定义是分开的。

实验一:ratio 越界是静默钳位

"ratio": "1 | 2" 改成 "1 | 3"(超出 1:2 下限),重新构建、装机:

Row [0,182,933,317]     ← 左栏列表行,右边界 933

和 1:2 一模一样。2800 / 3 = 933.33,取整 933。

**没有任何 error、没有 warning、hvigor 全绿。**越界值被静默替换成最近的合法边界。这类"配置写了但没生效、也不告诉你"的行为,只能靠量右边界发现——所以我现在写完配置一定会 dump 一次 ui layout

实验二:isDraggable 与 ratio 是互斥的,不是"后者失效"

文档原文是"开启后 ratio 字段失效"。实测不是失效,是构建期直接拒绝

> hvigor ERROR: 00303038 Configuration Error
Error Message: Schema validate failed, at file: ...\easy_go.json
  params: { failingKeyword: 'then' }

failingKeyword: 'then' 正好对应上面那条条件 Schema。要拖拽,就得把 ratio 整个删掉。

删掉 ratio、只留 "isDraggable": true 之后,默认落点变成 1:1:

Row [0,182,1400,317]    ← 2800 / 2 = 1400

然后拖分割线,三档吸附全部实测到位:

手势 左栏右边界 比例
默认 1400 1:1
向右拖到 2200 1866 2:1(2800×2/3=1866.7)
向左拖到 300 933 1:2
再拖回中间 1400 1:1

吸附点只有这三档,中间位置停不住。也就是说"用户自定义比例"这件事不存在,能做的是"三选"。

实验三:右栏页面到底知道自己多宽

这是我认为最有用的一个探针。在右栏页面根节点上挂 onAreaChange,同时读 UIContext.isEasySplit()

.onAreaChange((_o: Area, n: Area) => {
  this.diag = `isEasySplit=${this.getUIContext().isEasySplit()}`
    + `  本页宽度=${Math.round(n.width as number)}vp`;
})

四种状态各测一次,屏幕宽度恒为 1318vp:

配置 / 页面 左栏 px 本页宽度 isEasySplit
ratio: "1 | 2" 右栏详情 933 878vp true
isDraggable 拖到 1:1 1400 658vp true
isDraggable 拖到 2:1 1866 439vp true
fullScreenPages 投资笔记页 1318vp false

几个能直接拿去用的结论:

  • 878 / 1318 = 0.666,658 / 1318 = 0.499,439 / 1318 = 0.333。页面拿到的宽度和视觉分栏比例严格一致,没有隐藏 padding,可以放心按 vp 写响应式。
  • 全屏页 isEasySplit 返回 false,且宽度等于整屏。这个布尔值是目前唯一可靠的运行时判据——文档的常见问题里也承认"运行时无法仅通过配置预知运行平行视界分栏模式"。
  • 2:1 时右栏只剩 439vp(934px)。同一套四列指标栅格,列起点从 1:2 的 968 / 1418 / 1867 / 2317(间距 450px)压到 1901 / 2117 / 2334 / 2550(间距 216px),压缩率 52%。四列既没换行也没截断,但数字之间的留白从「能喘气」变成「贴面」。要不要开 isDraggable,取决于右栏在 216px 间距下还可不可读,而不是能不能显示出来。

第 4 步:全屏页与路由交接

右栏里点"打开投资笔记"会进 fullScreenPages 配置的页面,实测节点横跨 x=76 … 2758,铺满整个 2800 宽窗口,分栏自动退出。适合放长表格、全屏 K 线、富文本编辑器——这些在 1:2 右栏里都会挤变形。

左栏切换标的时,右栏同步更新(导航模式的定义行为)。实测点第三行"紫金矿业",右栏标题节点从

Text [1066,113,1228,160] "贵州茅台"   →   "紫金矿业"

同步换掉,左栏列表位置不动。这就是 mode: 1mode: 0 的区别:导航模式左栏固定,购物模式右页不断左推、左栏显示次栈顶。购物模式我没测,三折叠上更常见。

真机数据汇总

项目 实测值
分栏生效条件 窗口 ≥ 600vp 且 宽/高 > 1.2(本机 1318vp、1.522)
1:2 时左栏 / 右栏 933px / 1867px,右栏页面 878vp
ratio 越界 静默钳到边界,无 error/warn
ratio + isDraggable 构建失败 00303038 / failingKeyword: then
拖拽吸附档位 933 / 1400 / 1866px 三档
全屏页 1318vp 铺满,isEasySplit() = false
左栏列表行高 135px(182 → 317 → 452)
右栏指标列间距 1:2 时 450px(968/1418/1867/2317);2:1 时 216px(1901/2117/2334/2550)

未覆盖:squareWindowMode(方形宽屏,三折叠 M 形态)、enableReducedContainerSize(虚拟容器,会改变页面拿到的窗口宽度口径)、购物模式路由。这三项需要对应形态的设备或改动较大,本轮没有数据,不下结论。

ratio 1:2 下的默认分栏,左栏 933px

点第三行后右栏同步为紫金矿业

isDraggable 默认 1:1,右栏页面自报 658vp

拖到 2:1:右栏 439vp,指标列间距被压到 216px

fullScreenPages 全屏页,isEasySplit=false、1318vp

总结

平行视界适合的是"列表 + 详情"这类天然两段式的业务:行情、邮件、IM、订单、商品。它不需要改一行业务代码,一份 JSON 就能让竖屏应用在大屏上不尴尬。

但它只解决"两个页面并排",不解决"一个页面自己会分栏"。右栏内容一旦超过两种宽度形态(比如那四列指标),要么把 isDraggable 关掉锁死比例,要么老老实实做响应式。上表里的 439vp 就是那条分界线。

💡 一句话ratio 越界是静默钳位的,别信"配了就是生效"——在右栏挂一个 onAreaChangeisEasySplit(),10 秒钟就能确认分栏到底有没有起来。

参考文献

  1. 平行视界(最佳实践):https://developer.huawei.com/consumer/cn/doc/best-practices/bpta-easygo-parallel
  2. 多设备窗口形态:https://developer.huawei.com/consumer/cn/doc/best-practices/bpta-multi-device-window
  3. module.json5 配置文件:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/module-configuration-file
  4. UIContext(isEasySplit)API 参考:https://developer.huawei.com/consumer/cn/doc/harmonyos-references/arkts-apis-uicontext-uicontext
  5. 兼容方案目录:https://developer.huawei.com/consumer/cn/doc/best-practices/bpta-compatible-scheme