HarmonyOS 多设备短视频开发 : 17 — Navigation 路由与 NavPathStack
17 — Navigation 路由与 NavPathStack
一、引言
短视频应用中"视频页 → 评论区 / 个人主页"的跳转高频且要求无闪烁。HarmonyOS 的 Navigation 配合 NavPathStack 提供声明式路由能力。本项目在 default 产品层把 Navigation 作为导航根容器,通过 @Provider 下发全局导航栈,实现 Stack/Split 双模式切换与左右手持布局自适应。本文基于products/default/src/main/ets/view/Index.ets、features/multishortvideoindividual/src/main/ets/view/IndividualByRouter.ets与 route_map.json 展开。
二、Navigation 容器与全局导航栈
NavPathStack 在根组件创建,通过 @Provider 向子树共享,子组件用 @Consumer 拿到同一个栈对象:
@Entry @ComponentV2 struct Index { @Provider('pathStack') pathStack: NavPathStack = new NavPathStack(); @Provider('showSideComment') showSideComment: boolean = false; @Provider('showSideIndividual') showSideIndividual: boolean = false; build() { Navigation(this.pathStack) { MSVTabs({ data: this.data, ... }) } .navBarWidthRange([new WidthBreakpointType<number>(410, 410, 700, 700).getValue(this.windowInfo.widthBp), '100%']) .navBarWidth(new WidthBreakpointType<number>(410, 410, 700, 700).getValue(this.windowInfo.widthBp)) .hideBackButton(true) .hideTitleBar(true) .divider(null) .navBarPosition(this.holdingHandStatus === motion.HoldingHandStatus.RIGHT_HAND_HELD ? NavBarPosition.Start : NavBarPosition.End) .mode(this.showSideComment || this.showSideIndividual ? NavigationMode.Split : NavigationMode.Stack) .enableModeChangeAnimation(false) } }要点:navBarWidth 随断点变化(XS/SM 用 410vp,MD/LG 用 700vp);navBarPosition 跟随持握状态;mode 由侧面板开关动态决定,enableModeChangeAnimation(false) 防闪跳。
三、NavDestination 与路由注册
路由目标用 NavDestination 声明(IndividualByRouter.ets):
@Builder export function IndividualByRouterBuilder() { IndividualByRouter() } @ComponentV2 export struct IndividualByRouter { @Consumer('pathStack') pathStack: NavPathStack = new NavPathStack(); @Consumer('showSideIndividual') showSideIndividual: boolean = false; aboutToAppear(): void { this.pathStack.disableAnimation(this.windowInfo.widthBp < WidthBreakpoint.WIDTH_MD ? false : true); } build() { NavDestination() { Individual() } .hideTitleBar(true) .hideBackButton(true) .onReady((context: NavDestinationContext) => { this.pathStack = context.pathStack; // 绑定页面级导航上下文 }) } }路由映射在模块resources/base/profile/route_map.json注册,buildFunction 与导出的 @Builder 同名:
{ "routerMap": [ { "name": "IndividualByRouter", "pageSourceFile": "src/main/ets/view/IndividualByRouter.ets", "buildFunction": "IndividualByRouterBuilder" } ] }default 产品层另注册了 SplitComment,各产品模块各自维护 route_map.json,路由互不干扰。
四、入栈、出栈与参数传递
视频页(AdaptiveVideo.ets)按断点选择跳转形态:
if (this.windowInfo.widthBp > WidthBreakpoint.WIDTH_SM) { this.showSideComment = true; // 大屏:分栏侧开 this.pathStack.pushPathByName('SplitComment', null); // 入栈,可携带参数 } else { this.showComment = true; // 小屏:半模态 } // 关闭侧面板 this.pathStack.pop(); this.showSideComment = false;pushPathByName 的第二参可传任意对象,目标页在 aboutToAppear/onReady 中读取;onReady 回调返回 NavDestinationContext,其中的 pathStack 支持页面内返回与结果回传。
五、Stack/Split 双模式与多设备差异
NavigationMode.Stack 全屏压栈;Split 分栏时主内容区保持显示,NavDestination 作为右侧面板展开。四种产品差异如下:
| 产品 | 容器 | navBarWidth | 分栏策略 |
| default | Navigation + MSVTabs | 410/700vp 按断点 | 评论、个人主页 Split |
| pc | SideBarContainer 内嵌 Navigation | '66%' | 同上 |
| tv | Navigation + TvTabs | 880vp | 同上 |
| wearable | Navigation + 精简 Tabs | 全宽 | 始终 Stack |
TV 端(products/tv/src/main/ets/view/Index.ets)同样以 Navigation 为根容器,区别是页签用 TvTabs、navBarWidth 固定 880vp、无navBarPosition切换,其余 Stack/Split 逻辑与 default 一致。
六、点击主区关闭侧面板
侧面板打开时主内容区拦截点击,在 onGestureRecognizerJudgeBegin 中收起(REJECT 吞掉点击,防止误触播放):
.gesture(TapGesture()) .onGestureRecognizerJudgeBegin((event, current) => { if (current && (current.getType() === GestureControl.GestureType.TAP_GESTURE || current.getType() === GestureControl.GestureType.CLICK)) { if (this.showSideComment || this.showSideIndividual) { this.pathStack.pop(); this.showSideComment = false; this.showSideIndividual = false; return GestureJudgeResult.REJECT; } } return GestureJudgeResult.CONTINUE; })配合hitTestBehavior(HitTestMode.Block),被拦截的点击不会透传给视频播放层。
七、总结与最佳实践
- NavPathStack 全局唯一,用 @Provider/@Consumer 传递避免层层透传;页面内导航用 onReady 的页面级栈。
- 路由统一注册到 route_map.json,buildFunction 与导出 @Builder 同名,保证映射可维护。
- 模式切换由业务开关驱动,配合 enableModeChangeAnimation(false) 防止闪烁。
- 大屏用 Split 分栏、小屏用半模态/Stack,断点驱动形态统一收敛在跳转处。
- navBarPosition 跟随持握状态,让折叠屏/平板单手场景更友好。
