HarmonyOS 7 EasyGo + ArkUI Navigation:平行视界导航模式的列表选中态、右栏路由栈与全局返回收口【鸿蒙心迹】
这次不写“平行视界怎么打开”,而是把问题往真实阅读类 App 里再推进一步:左边文章列表固定,右边详情可以继续点相关推荐;当右栏已经压入第二层详情以后,用户执行全局返回,应该只退右栏,不应该把左边选中的文章一起重置。

我给这次 Demo 起名叫 ParallelReaderLab,页面叫 Article Desk。
为了让正文、日志和图片完全对得上,这次固定一组运行状态:
- Mode:
NAVIGATION - Split Ratio:
1:2 - Selected:
article_20261001_12 - Right Stack:
2 → 1 - Related:
related_20261001_03 - Last Back:
2 → 1
HarmonyOS 7(API 26)的平行视界当前提供购物模式和导航模式两类典型交互。导航模式的特点很适合阅读、邮件、IM 这类场景:左侧导航或列表保持稳定,右侧内容区继续切换;官方说明里也特别强调了全局返回时右侧页面按层级回退,以及 1:1、1:2、2:1 等分栏比例。
我这篇真正关心的不是“页面能不能分成左右两块”,而是三个状态能不能拆开:
左栏选中态、右栏路由栈、系统平行视界布局状态。
一、先把“左边选中了谁”从右栏页面里拿出来
最开始的实现很普通。
点击列表项以后,直接 push 一个详情页:
private openArticle(articleId: string): void {
this.navPathStack.pushPathByName('ArticleDetail', {
id: articleId
})
}
手机单栏时看不出问题。
但进入平行视界以后,左边列表和右边详情同时存在。此时 ArticleDetail 如果自己维护“当前文章 ID”,左边 ListItem 又维护一份 selected 状态,两边很快就会漂移。
例如左边已经点中 article_20261001_12,右边详情继续点相关推荐 related_20261001_03。此时右栏路由已经两层,但左栏仍然应该高亮原始入口文章,而不是突然跟着右栏相关推荐切换。
所以第一步,我把左栏入口状态单独放进一个轻量状态模型。
export class ArticleRouteState {
selectedArticleId: string = 'article_20261001_12';
rightDepth: number = 1;
selectFromLeft(articleId: string): void {
this.selectedArticleId = articleId;
}
updateDepth(depth: number): void {
this.rightDepth = depth;
}
}
这里的关键不是这个类有多复杂,而是它明确了一件事:
selectedArticleId 描述的是左栏当前入口,不等于右栏栈顶页面 ID。
这两个值在第一层详情时可能相同,进入相关推荐以后就不一定相同。
如果一开始把两个概念混成一个变量,后面处理返回时一定会出问题。
二、Navigation 只管理右栏层级,不替左栏决定高亮项
平行视界官方示例本身就是基于 Navigation 路由来承载应用页面关系。
我这次也沿用这个方向,但给右侧单独维护 NavPathStack。
这段代码解决的问题,是左栏点击文章以后更新入口状态,同时把右侧详情放进路由栈。
@Entry
@Component
struct ArticleDesk {
private navPathStack: NavPathStack = new NavPathStack();
@State private selectedArticleId: string =
'article_20261001_12';
private openFromLeft(articleId: string): void {
this.selectedArticleId = articleId;
this.navPathStack.clear();
this.navPathStack.pushPathByName(
'ArticleDetail',
{ id: articleId }
);
console.info(
`Selected: ${articleId}, depth=1`
);
}
build() {
Row() {
ArticleList({
selectedId: this.selectedArticleId,
onSelect: (id: string) => {
this.openFromLeft(id);
}
})
Navigation(this.navPathStack) {
ArticleEmpty()
}
}
}
}
我在“左栏重新选择文章”时会清掉右栏原来的相关推荐层级。
这是一个产品判断。
如果用户已经在文章 A 的相关推荐第三层,此时直接从左边点文章 B,我希望右边重新从 B 的第一层详情开始,而不是让 B 接在 A 的旧路由链后面。
正式项目当然也可以保留历史,但必须把行为定义清楚。
三、右侧继续点相关推荐时,不改左侧 selected
接下来就是这次最核心的边界。
article_20261001_12 的详情页里又点击了 related_20261001_03。
这段代码只做右侧 push:
private openRelated(relatedId: string): void {
this.navPathStack.pushPathByName(
'RelatedDetail',
{ id: relatedId }
);
console.info(
`Route push: ${relatedId}, ` +
`depth=${this.navPathStack.size()}`
);
}
此时运行状态变成:
Selected: article_20261001_12
Right top: related_20261001_03
Right depth: 2
注意,我没有写:
this.selectedArticleId = relatedId
因为相关推荐是右栏内部的继续阅读,不是用户从左栏重新选择入口。
这个区别放到小屏手机里没那么明显,但进入平行视界以后,左边一直在用户眼前,高亮错一次就会非常突兀。
四、全局返回真正应该回的是右栏层级
HarmonyOS 7 平行视界导航模式的一个重要体验,就是全局返回与右侧页面层级保持一致。
在我的业务状态里,这意味着:
depth = 2 时返回,右栏从相关推荐回到入口文章;左栏 selected 不变。
我给页面内部的路由收口写成一个方法,方便调试和测试。
private backRightPane(): boolean {
const before = this.navPathStack.size();
if (before <= 1) {
return false;
}
this.navPathStack.pop();
const after = this.navPathStack.size();
console.info(
`GlobalBack: depth ${before} -> ${after}`
);
console.info(
`Selected kept: ${this.selectedArticleId}`
);
return true;
}
这段代码不是用来“替代系统平行视界返回机制”。
系统侧是否触发分栏回退、页面如何呈现,仍然由当前 EasyGo / 平行视界配置和 Navigation 路由共同决定。
我这里做的是业务收口:右栏路由变化以后,明确保证左栏入口状态不被顺手重置。
最终这次日志固定为:
EasyGo mode: NAVIGATION
Split ratio: 1:2
Selected: article_20261001_12
Route push: related_20261001_03 depth=2
GlobalBack: depth 2 -> 1
Selected kept: article_20261001_12

DevEco 图里我特意让四块信息同时出现。
左边是 ParallelReaderLab 工程结构,中间是 ArticleDesk.ets,右边模拟器已经处于平行视界阅读状态,底部 HiLog 则直接验证 depth 2 → 1 以后 selected 仍然没变。
五、为什么我没有把平行视界配置 JSON 大段贴出来
这次我刻意没有把文章写成 EasyGo 配置字段说明书。
原因是 HarmonyOS 7 官方当前已经提供专门的平行视界示例,示例以 Navigation 路由的购物类应用为载体,覆盖主页与关联页、路由模式、过渡页面、全屏请求、横屏请求、虚拟容器以及应用内分屏等典型场景。
这些配置应该直接以当前 SDK 和官方样例为准。
本文更想处理的是配置完成以后,应用内部仍然需要自己维护的业务状态。
很多适配问题并不是 EasyGo 没生效,而是:
- 左栏 selected 跟右栏栈顶绑定;
- 每次路由变化都刷新整份列表;
- 返回时把详情和入口状态一起清空;
- 大屏有双栏,小屏又维护另一套完全不同的数据模型。
真正难维护的是这些。
六、1:2 分栏只是显示策略,不应该写进业务逻辑
这次 Demo 固定使用 1:2。
左边是文章目录,右边是长文阅读,右侧需要更大的内容宽度,所以这个比例比较自然。
HarmonyOS 7 平行视界当前也支持 1:1、1:2、2:1 等比例以及拖拽调整。
但我不会在业务层写:
if (ratio === '1:2') {
// selected 逻辑
}
比例只应该影响布局。
文章是谁、右栏深度多少、返回以后哪个 ID 继续高亮,不应该因为用户拖了一下分割线就重新计算。
这也是我现在判断多设备适配质量的一个标准:
视觉状态变化可以很多,业务状态变化要尽量少。
七、最终运行图验证的不是“左右两栏”,而是返回以后状态没乱
最终手机 / 折叠屏运行图固定在一次全局返回之后。

此时调试卡片里显示:
Mode: NAVIGATION
Ratio: 1:2
Right depth: 1
Selected: article_20261001_12
Last back: 2 -> 1
右边已经从 related_20261001_03 回到 article_20261001_12 的正文。
左边高亮仍然是同一篇文章。
如果只看“页面是不是双栏”,这个 Demo 十几分钟就能搭出来。
真正跑通以后,我反而更在意这几次操作:
- 左栏连续点三篇文章;
- 右栏连续进入两级相关推荐;
- 全局返回两次;
- 调整分栏比例;
- 再从左栏选择新文章。
每一步都检查 selected、right depth 和当前页面 ID。
只要三者能一直对上,平行视界才不是“把两张页面摆在一起”。
八、这次真正留下的是两套互不抢状态的导航
写到最后,我把整个页面理解成两套层级。
第一套是左侧入口选择:
article_20261001_12
它代表用户从目录里选中了什么。
第二套是右侧内容路由:
ArticleDetail
└── RelatedDetail
它代表用户在当前入口里读到了哪一层。
EasyGo / 平行视界负责把这种关系变成适合大屏的系统级分栏体验,Navigation 负责页面路由,而应用自己要保证两套状态不要互相覆盖。
以后如果继续扩展,我会再补两个测试:
- 全局返回到 depth=1 后再返回,验证是否退出当前详情或回到上一级业务入口;
- 从平行视界切回单栏以后,验证同一份 selected 和 right path 能否继续使用。
这两项比再调一个漂亮分栏比例更值得做。
参考资料
- HarmonyOS 7(API 26)平行视界能力解读
https://developer.huawei.com/consumer/cn/forum/topic/0201221235973021541 - HarmonyOS 官方示例代码:平行视界(Navigation 路由)
https://developer.huawei.com/consumer/cn/samples/ - 多设备通用适配指南
https://developer.huawei.com/consumer/cn/multidevice/adaptive-apps/
更多推荐




所有评论(0)