# 左自选右行情的单应用多任务 # 智绘鸿蒙·如 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 位过不了校验 |

还有一条容易忽略的：`deviceConfig` 层 `displayModeOptions` 与 `settingDisplayOptions` 互斥，同一个设备类型节点里只能出现一个。

这个 demo 用的是导航模式：

```json
{
  "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()`：

```typescript
.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: 1` 和 `mode: 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](assets/01-split.png)

![点第三行后右栏同步为紫金矿业](assets/03-switch.png)

![isDraggable 默认 1:1，右栏页面自报 658vp](assets/04-1to1.png)

![拖到 2:1：右栏 439vp，指标列间距被压到 216px](assets/05-2to1.png)

![fullScreenPages 全屏页，isEasySplit=false、1318vp](assets/02-fullscreen.png)

## 总结

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

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

> 💡 **一句话**：`ratio` 越界是静默钳位的，别信"配了就是生效"——在右栏挂一个 `onAreaChange` 读 `isEasySplit()`，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
