跳到主要内容

04Navigation跳转

Navigation跳转

需求:由Index页面跳转到Home页面/组件

bug点:API19版本中**,需要使用模拟器**完成本案例。预览器不支持。其他版本同学们自行测试预览器是否支持。

跳转页Index(源页面)

1-Navigation组件

必须使用Navigation作为根容器组件。

@Entry
@Component
struct Index {
build() {
Navigation() {
Button('点击跳转Home组件')
}
.width('100%')
.height('100%')
}
}

2- NavPathStack 对象

需要在源页面中设置 NavPathStack 对象

  pagePath:NavPathStack = new NavPathStack()

3-关联

需要将该 NavPathStack 对象跟Navigation组件关联起来

 pagePath:NavPathStack = new NavPathStack()

Navigation(this.pagePath){

}

4-跳转页面

下面的跳转功能代码,name属性对应的配置值,还没写呢,此处是伪代码。

  Button('点击跳转Home组件')
.onClick(() => {
this.pagePath.pushPath({name:"目标页面的配置名称后续回来补上????"})
})

目标页面Home,

1-NavDestination组件

必须使用NavDestination作为根容器组件。

@Component
struct Home {
build() {
NavDestination(){
Column({ space: 30 }) {
Text('Home组件效果')
}
.width('100%')
.height('100%')
}
}
}

2-入口函数

需要一个入口函数,方便在后续的配置文件中使用。该函数在组件之外的位置。可以写在当前文件的第一行。

@Builder
export function homeBuilder(){
Home()
}

配置文件

设置配置文件,源页面的name值和目标页面的路径,均需要由配置文件关联。

1 - module.json5配置文件

找到src/main/module.json5配置文件,未修改前的默认样子如下

module.json5修改之前

2 - 添加 routerMap配置项

追加配置项:"routerMap": "$profile:route_map"。追加好后的样子如下图:

module.json5修改之后

3 - 创建route_map.json

src/main/resources/base/profile目录下创建route_map.json

4 - 在route_map.json 中添加配置项如下

{
"routerMap": [
{
"name": "Home",
"pageSourceFile": "src/main/ets/view/Home.ets",
"buildFunction": "homeBuilder"
}
]
}

Home文件与配置项的关系


页面文件代码最终效果

1 - Index完整代码

@Entry
@Component
struct Index {
pagePath:NavPathStack = new NavPathStack()
build() {
Navigation(this.pagePath) {
Button('点击跳转Home组件')
.onClick(() => {
this.pagePath.pushPath({name:"Home"})
})
}
.width('100%')
.height('100%')
}
}

2 - Home完整代码

@Builder
export function homeBuilder(){
Home()
}

@Component
struct Home {
build() {
NavDestination(){
Column({ space: 30 }) {
Text('Home组件效果')
}
.width('100%')
.height('100%')
}
.hideBackButton(true) // 取消 NavDestination 默认的返回箭头
}
}

组件导航详细知识

组件导航和页面路由概述

基本概念

页面是指由布局、组件、交互逻辑等构成的可视化交互单元,承载着特定功能逻辑与信息展示,是用户与应用进行操作交互的核心界面载

体。一个完整的应用往往由多个页面组成,组件导航(Navigation)和页面路由(@ohos.router)均提供了应用内的页面跳转能力。

  • 在组件导航(Navigation)框架下,“页面"通过NavDestination组件承载,特指一个NavDestination组件包含的内容。

  • 在页面路由(@ohos.router)框架下,“页面"特指@Entry装饰的自定义组件。

架构差异

组件导航(Navigation)将页面放在Navigation组件内部进行跳转,具备更强的一次开发多端部署能力,可以进行更加灵活的页面栈操作。推荐使用组件导航(Navigation)来实现页面跳转以及组件内的跳转,以获得更佳的使用体验。

架构差异

组件导航优势

  1. 接口上显式区分标题栏、内容区和工具栏,实现更加灵活的管理和UX动效能力;

  2. 显式提供路由容器概念,由开发者决定路由容器的位置,支持在全模态、半模态、弹窗中显示;

  3. 基于通用UIBuilder能力,由开发者决定页面别名和页面UI对应关系,提供更加灵活的页面配置能力;

  4. 整合UX设计和一次开发多端部署能力,默认提供统一的标题显示、页面切换和单双栏适配能力;

  5. 基于组件属性动效和共享元素动效能力,将页面切换动效转换为组件属性动效实现,提供更加丰富和灵活的切换动效;

  6. 开放了页面栈对象,开发者可以继承,能更好的管理页面显示。

组件导航

组件导航的基本使用

基本概念

组件导航(Navigation)主要用于实现Navigation页面(NavDestination)间的跳转,支持在不同Navigation页面间传递参数,提供灵活的跳转栈操作,从而更便捷地实现对不同页面的访问和复用。

组件导航基本概念

// 组件导航基本使用
@Entry
@Component
struct ComponentNavigation {
// 1.设置导航控制器
navPathStack: NavPathStack = new NavPathStack()

// 准备一个可以展示的UI结构(模仿页面)
@Builder
myNavDestination(name: string) {
// 需要注意的是这里展示的UI必须 被 NavDestination 包裹
NavDestination(){
// 根据name值展示对应UI
if (name === 'pageA') {
Text('我是新页面A')
}
}

}

build() {
Column({ space: 30 }) {
// 绑定导航控制器
Navigation(this.navPathStack) {
Text('NavNavigation基本使用')
Button('去页面A')
.onClick(() => {
// 3.路由跳转
this.navPathStack.pushPath({ name: 'pageA' })
})
}
// 2. 设置路由导航。注意不加小括号,不是调用函数。而是告知要用哪个UI结构
.navDestination(this.myNavDestination)
}
.width('100%')
.height('100%')
}
}

组件导航的显示模式

Navigation组件通过mode属性设置页面的显示模式。

自适应模式(默认):小于600vp时单栏,大于600vp时分栏。

单栏模式:单栏模式适用于窄屏设备,发生路由跳转时,整个页面都会被替换。

分栏模式:分栏模式适用于宽屏设备,分为左右两部分,发生路由跳转时,只有右边子页会被替换。

Navigation(this.navPathStack){}
// 设置分栏模式
.mode(NavigationMode.Stack)// 不论屏幕尺寸宽窄,都单栏
.mode(NavigationMode.Split)// 不论屏幕尺寸宽窄,都分栏,除非窄到装不下
.mode(NavigationMode.Auto)// 小于600vp时单栏,大于600vp时分栏。

组件导航的标题栏

标题栏在界面顶部,用于呈现界面名称和操作入口,Navigation组件通过title属性设置主标题,通过titleMode属性设置标题栏模式。

Mini模式:普通型标题栏,用于一级页面不需要突出标题的场景。

Full模式:单栏模式适用于窄屏设备,发生路由跳转时,整个页面都会被替换。

Free模式:当内容为满一屏的可滚动组件时,标题随着内容向上滚动而缩小(子标题的大小不变、淡出)。向下滚动内容到顶时则恢复原样。

Navigation(this.navPathStack){}
.title('标题') // 也可以用于NavDestination设置子标题
.titleMode(NavigationTitleMode.Mini)
.titleMode(NavigationTitleMode.Full)
.titleMode(NavigationTitleMode.Free)
.hideTitleBar(true) //隐藏标题 也可以用于NavDestination隐藏子标题

组件导航的菜单栏

菜单栏位于Navigation组件的右上角,可以通过menus属性进行设置,支持Array<NavigationMenultem>CustomBuilder两种参数类型。

使用Array<NavigationMenultem>类型时,竖屏最多支持显示3个图标,横屏最多支持显示5个图标,多余的图标会被放入自动生成的更多图标。

可以使用action函数完成对应的点击功能。

 // 设置菜单栏(前提是不能隐藏了标题栏,需要让标题栏显示出来)
.menus([
{value:'编辑',icon:$r('sys.media.ohos_ic_public_edit')},
{value:'扫码',icon:$r('sys.media.ohos_ic_public_scan'),action: () => {
promptAction.openToast({message:'拉起扫码业务界面进行扫码'})
}},
{value:'wifi',icon:$r('sys.media.wifi_router_fill')},
{value:'搜索',icon:$r('sys.media.AI_search')}
])

组件导航的工具栏

工具栏位于Navigation组件的底部,可以通过toolbarConfiguration属性进行设置。可以设置工具栏内容value和工具栏属性options

工具栏内容支持Array<Toolbarltem>CustomBuilder两种参数类型。

工具栏属性包含工具栏背景颜色、工具栏背景模糊样式及模糊选项、工具栏背景属性、工具栏布局方式、是否隐藏工具栏的文本、工具栏

更多图标的菜单选项。

      //   设置工具栏
.toolbarConfiguration(
// 内容
[
{ value: '首页', icon: $r('sys.media.leave_home_fill') },
{ value: '分享', icon: $r('sys.media.ohos_ic_public_share'),action: () => {
//跳转到分享页面
// this.navPathStack.pushPath({name:'分享页面标记名程例如share'})
this.navPathStack.pushPath({name:'pageA'})
} },
{ value: '我的', icon: $r('sys.media.person_shield') }
],
// 属性
{
//工具栏背景颜色,不设置时为系统默认颜色。
backgroundColor: '#ffd49494',
// 工具栏背景模糊样式,不设置时关闭背景模糊效果。
//Thin 轻薄材质模糊。 Regular普通厚度材质模糊。Thick厚材质模糊。BACKGROUND_THIN近距景深模糊。BACKGROUND_REGULAR中距景深模糊。BACKGROUND_THICK远距景深模糊。。。
backgroundBlurStyle: BlurStyle.COMPONENT_ULTRA_THICK,
/* 设置工具栏布局方式。默认值:BarStyle.STANDARD
* STANDARD指定该模式的标题栏或工具栏与内容区采用上下布局。
* STACK指定该模式的标题栏或工具栏与内容区采用层叠布局,标题栏或工具栏布局在内容区上层。
* SAFE_AREA_PADDING将指定该模式的标题栏或工具栏设置为组件级安全区。
* */
barStyle:BarStyle.SAFE_AREA_PADDING,
// 工具栏背景模糊选项。
backgroundBlurStyleOptions:{
policy:BlurStyleActivePolicy.FOLLOWS_WINDOW_ACTIVE_STATE //模糊效果跟随窗口焦点状态变化,非焦点不模糊,焦点模糊。
},
// 设置工具栏背景属性包括:模糊半径,亮度,饱和度,颜色等。
backgroundEffect:{
//模糊半径,取值范围:[0, +∞),默认为0。
radius:0,
// 饱和度,取值范围:[0, +∞),默认为1。推荐取值范围:[0, 50]。
saturation:10
// ...
}
}
)

组件导航路由操作

基本概念:Navigation路由相关的操作都是基于导航控制器NavPathStack提供的方法进行,每个Navigation都需要创建并传入一个NavPathStack对象,用于管理页面。主要涉及页面跳转、页面返回、页面替换、页面删除、参数获取、路由拦截等功能。

页面跳转:

NavPathStack通过Push相关的接口 去实现页面跳转的功能,主要分为以下三类:

  1. 普通跳转,通过页面的name去跳转,并可以携带param。
//1.普通跳转
this.navPathStack.pushPath({ name: "PageOne", param: "PageOne Param" })
this.navPathStack.pushPathByName("PageOne", "PageOne Param");
  1. 带返回回调的跳转,跳转时添加onPop回调,能在页面出栈时获取返回信息,并进行处理。
//2.带返回回调跳转
this.navPathStack.pushPathByName('PageOne', "PageOne Param", (popInfo) => {
console.info('Pop page name is: ' + popInfo.info.name + ', result: ' + JSON.stringify
(popInfo.result));
});
  1. 带错误码的跳转,跳转结束会触发异步回调,返回错误码信息。
//3.带错误码跳转
this.navPathStack.pushDestination({ name: "PageOne", param: "PageOne Param" })
.catch(() => {
console.error('Push destination failed');
}).then(() => {
console.info('Push destination succeed.');
});

页面返回:

NavPathStack通过Pop相关接口去实现页面返回功能。

  1. 使用导航控制器的pop方法返回上一个页面。
  2. 使用导航控制器popToName返回上一个指定名称的页面。
  3. 使用popTolndex返回指定索引l的页面。
  4. 使用clear清除栈中所有页面,实现返回根页面。
//返回到上一页
this.navPathStack.pop();
//返回到上一个PageOne页面
this.navPathStack.popToName("PageOne");
//返回到索引为1的页面
this.navPathStack.popToIndex(1);
//返回到根首页(清除栈中所有页面)
this.navPathStack.clear();

页面替换:

NavPathStack通过Replace相关接口去实现页面替换功能。原路由在路由栈中不会留记录。

  • replacePath

  • replacePathByName

  • replaceDestination

Button('替换')
.onClick(() => {
//将栈顶页面替换为Page0ne
this.navPathStack.replacePath({ name: "PageOne", param: "PageOne Param" });
this.navPathStack.replacePathByName("PageOne", "PageOne Param");
// 带错误码的替换,跳转结束会触发异步回调,返回错误码信息
this.navPathStack.replaceDestination({
name: "PageOne",
param: "Page0ne Param"
})
.catch(() => {
console.error('Replace destination failed');
}).then(() => {
console.info('Replace destination succeed.');
})
})

页面删除:

NavPathStack通过Remove相关接口去实现删除路由栈中特定页面的功能。

  • removeByName

  • removeBylndexes

  • removeByNavDestinationld

Button('删除')
.onClick(() => {
//删除栈中name为PageOne的所有页面
this.navPathStack.removeByName("PageOne");
//删除指定索引的页面
this.navPathStack.removeByIndexes([1, 3, 5]);
//删除指定id的页面
this.navPathStack.removeByNavDestinationId("1") ;
})

页面移动:

NavPathStack通过Move相关接口去实移动路由栈中特定页面到栈顶的功能。

  • moveToTop

  • movelndexToTop

Button('移动')
.onClick(() => {
// 移动栈中name为PageOne的页面到栈顶
this.navPathStack.moveToTop("PageOne");
//移动栈中索引为1的页面到栈顶
this.navPathStack.moveIndexToTop(1);
})

参数获取:

  • NavDestination子页第一次创建时会触发onReady回调,可以获取此页面对应的参数。

  • NavDestination组件中可以通过设置onResult接口,接收返回时传递的路由参数。

其他业务场景,可以通过主动调用NavPathStackGet相关接口去获取指定页面的参数

  • getAllPathName获取栈中所有页面name集合

  • getParamBylndex获取指定索引l的页面参数

  • getParamByName获取页面的参数

  • getlndexByName获取页面的索引集合

Button('获取栈中所有页面name集合')
.onClick(()=>{
//获取栈中所有页面name集合
this.navPathStack.getAllPathName();
Button('获取索引为1的页面参数')
.onClick(()=>{
//获取索引为1的页面参数
this.navPathStack.getParamByIndex(1);
})
Button('获取PageOne页面的参数')
.onClick(()=>{
//获取PageOne页面的参数
this.navPathStack.getParamByName("PageOne");
})
Button('获取PageOne页面的索引集合')
.onClick(()=>{
//获取PageOne页面的索引集合
this.navPathStack.getIndexByName("PageOne");
})

路由拦截:

NavPathStack提供了setlnterception方法,用于设置Navigation页面跳转拦截回调。该方法需要传入一个

Navigationlnterception对象,该对象包含三个回调函数:

  1. willShow页面跳转前回调,允许操作栈,在当前跳转生效。

  2. didShow页面跳转后回调,在该回调中操作栈会在下一次跳转生效。

  3. modeChange页面Navigation单双栏显示状态发生变更时触发该回调。

跳转前回调:

willShow:页面跳转前回调的回调函数。该回调有四个参数,分别是:

  1. 跳转前的页面信息

  2. 跳转后的页面信息

  3. 当前的跳转类型

  4. 页面跳转是否有动画

可以修改路由栈来实现路由拦截重定向的能力

跳转后回调:

didShow: 页面跳转后回调的回调函数。该回调有四个参数,分别是:

  1. 跳转前的页面信息

  2. 跳转后的页面信息

  3. 当前的跳转类型

  4. 页面跳转是否有动画

无论哪个回调,进入回调时,路由栈都已经发生了变化

案例应用:查看个人信息拦截到登录页面去
import { promptAction } from '@kit.ArkUI'

// 路由拦截案例
@Entry
@Component
struct MyNavigationInterception {
// 登录状态
@Provide isLogin: boolean = false
// 1.设置导航控制器
navPathStack: NavPathStack = new NavPathStack()

// 构建UI 路由子页面
@Builder
myNavDestination(name: string) {
if (name === 'user') {
User()
} else if (name === 'login') {
Login()
}
}

build() {
// 绑定导航控制器
Navigation(this.navPathStack) {
Button('查看用户信息')
.onClick(() => {
this.navPathStack.pushPath({ name: 'user' })
})
}
// 准备拦截
.onAppear(() => {
this.navPathStack.setInterception({
// 页面跳转前回调
willShow: (
from: NavDestinationContext | "navBar",
to: NavDestinationContext | "navBar",
operation: NavigationOperation,
isAnimated: boolean
) => {
//to 如果是 'navBar' 说明是去跟页面 navBar是根页面的名程
if (typeof to === 'string') {
return
}
if (to.pathInfo.name === 'user' && !this.isLogin) {
// 一旦进入回调函数,路由栈已经发生变化了
this.navPathStack.pop()
// 提示信息
this.getUIContext().getPromptAction().openToast({
message: '用户未登录,轻先登录'
})
// 跳转登录页面
this.navPathStack.pushPath({ name: 'login' })
}
},
didShow: (from,to,operation,isAnimated) => {
// 页面跳转后回调的回调函数
},
modeChange: (mode) => {
promptAction.openToast({ message: '当前分栏模式发生变化:' + mode })
}
})
})
.navDestination(this.myNavDestination)
}
}

@Component
struct User {
@Consume isLogin: boolean
navPathStack: NavPathStack | undefined = undefined

build() {
NavDestination() {
Text('用户信息:帝心')
Button('退出登录')
.onClick((event: ClickEvent) => {
this.isLogin = false
this.navPathStack?.pop()
})
}
.onReady((context) => {
this.navPathStack = context.pathStack
})
}
}

@Component
struct Login {
@Consume isLogin: boolean
navPathStack: NavPathStack | undefined = undefined

build() {
NavDestination() {
Button('你还没登录,点我登录')
.onClick(() => {
this.isLogin = true
// 提示信息
this.getUIContext().getPromptAction().openToast({
message: '登录成功'
})
//返回上一页
this.navPathStack?.pop()
})
}
.onReady((context) => {
this.navPathStack = context.pathStack
})
}
}

路由表配置

路由表:

路由表是实现页面跳转的核心机制,其本质是一个集中管理页面跳转规则的映射表。

Navigation支持通过router_map.json文件建立页面名称与页面组件的映射关系,在触发页面跳转时,传入需要路由的页面配置名称,

系统会自动完成路由模块的动态加载、页面组件构建,从而实现开发层面的模块解耦。

每个路由条目包含了名称、文件路径、构造函数、元数据四个核心字段

{
"routerMap": [
{
"name": "Home",
"pageSourceFile": "src/main/ets/view/Home.ets",
"buildFunction": "homeBuilder",
"data":{"description":"主页模块"}
}
{
"name": "login",
"pageSourceFile": "src/main/ets/view/Login.ets",
"buildFunction": "loginBuilder",
"data":{"description":"登录模块"}
}
]
}

配置步骤

  1. resources/base/profile目录下新建router_map.json文件

  2. module.json5添加路由表配置routerMap属性

  3. router_map.json添加路由配置信息

  4. 在跳转目标页面中,需要配置入口Builder函数,函数名称需要和router_map.json配置文件中的buildFunction保持一致

  5. 通过pushPath等接口进行跳转(navigation不用配置navDestination属性)

加载中...