开天辟地 HarmonyOS(鸿蒙) - 组件(导航类): Navigation(导航组件的导航)
开天辟地 HarmonyOS(鸿蒙) - 组件(导航类): Navigation(导航组件的导航)
示例如下:
pages\component\navigation\NavigationDemo2.ets
/*
* Navigation - 导航组件
* 包括顶部的导航栏,中部的内容区,底部的工具栏
*
* 本例用于演示导航组件的导航
* Navigation 是导航的根组件,导航到的目标页是 NavDestination 组件,导航栈中包含的是多个 NavDestination 组件(Navigation 组件不在导航栈中)
* 如果需要通过 Navigation 实现导航,则先要配置路由表
* 1、在 src/main/module.json5 中做类似如下的配置(指定路由表)
* {
* "module": {
* "routerMap": "$profile:route_map"
* }
* }
* 2、在 src/main/resources/profile/route_map.json 中配置路由表
* {
* "routerMap": [
* {
* // 路由的名称
* "name": "navigationDemo2_page1",
* // 指定一个页面地址,此页面中包含 buildFunction 指定的函数
* "pageSourceFile": "src/main/ets/pages/component/navigation/pages/NavigationDemo2_Page1.ets",
* // 指定一个 @Builder 函数的名称,其用于构造此路由指向的组件
* "buildFunction": "navigationDemo2_page1_builder"
* }
* ]
* }
*
* 注:本例演示的是如何通过路由表(允许非 @Entry 组件)做路由,如需要通过页面地址做路由(必须是 @Entry 组件)请参见 RouterDemo.ets 中的说明
*/
import { TitleBar, RadioBar } from '../../TitleBar';
import { LengthMetrics, SymbolGlyphModifier } from '@kit.ArkUI';
@Entry
@Component
struct NavigationDemo {
build() {
Column() {
TitleBar()
Tabs() {
TabContent() { MySample1() }.tabBar('导航的基础').align(Alignment.Top)
TabContent() { MySample2() }.tabBar('导航的拦截').align(Alignment.Top)
TabContent() { MySample3() }.tabBar('入栈的模式').align(Alignment.Top)
}
.scrollable(true)
.barMode(BarMode.Scrollable)
.layoutWeight(1)
}
}
}
@Component
struct MySample1 {
/*
* NavPathStack - 导航栈(实例化 Navigation 时指定一个 NavPathStack 对象,后续可以通过此 NavPathStack 对象实现导航)
* pushPath() - 将指定的页面入栈
* info - 入栈页面的信息(一个 NavPathInfo 对象)
* name - 目标路由的名称(即 routerMap 中的 name)
* param - 传给目标页的数据
* isEntry - 目标页是否是 @Entry 装饰的
* onPop - 目标页出栈且传回了数据时的回调(回调参数是一个 PopInfo 对象)
* info - 出栈的页的 NavPathInfo 对象
* result - 出栈的页传回的数据
* options - 选项(一个 NavigationOptions 对象)
* animated - 是否启用转场动画
* launchMode - 页面入栈的模式
* pushPathByName(name: string, param: Object, onPop: Callback<PopInfo>, animated?: boolean) - 将指定的页面入栈,参数的用法可以参考 pushPath() 的说明
* pushDestination(info: NavPathInfo, animated?: boolean) - 将指定的页面入栈,参数的用法可以参考 pushPath() 的说明
* 返回一个 Promise 对象
* pushDestinationByName(name: string, param: Object, animated?: boolean) - 将指定的页面入栈,参数的用法可以参考 pushPath() 的说明
* 返回一个 Promise 对象
* disableAnimation() - 是否禁用所有转场动画
* getParent() - 当 Navigation 嵌套 Navigation 时,可以获取当前 Navigation 的父 Navigation 的 NavPathStack 对象
*
* 注:本例导航到的页面请参见 component/navigation/pages/NavigationDemo2_Page1.ets
*/
navPathStack: NavPathStack = new NavPathStack()
@State message: string = ""
aboutToAppear(): void {
this.navPathStack.disableAnimation(false)
let parent_navPathStack = this.navPathStack.getParent()
}
build() {
Navigation(this.navPathStack) {
Column({space:10}) {
Button('pushPath').onClick(() => {
this.navPathStack.pushPath({
name: 'navigationDemo2_page1',
param: "abc",
isEntry: false,
onPop: (popInfo: PopInfo) => {
this.message += `onPop name:${popInfo.info.name}, result:${popInfo.result}\n`
},
}, {
animated: true,
launchMode: LaunchMode.STANDARD
})
})
Button('pushPathByName').onClick(() => {
this.navPathStack.pushPathByName(
'navigationDemo2_page1',
"abc",
(popInfo: PopInfo) => {
this.message += `onPop name:${popInfo.info.name}, result:${popInfo.result}\n`
},
true
)
})
Button('pushDestination').onClick(() => {
this.navPathStack.pushDestination({
name: 'navigationDemo2_page1',
param: "abc",
onPop: (popInfo: PopInfo) => {
this.message += `onPop name:${popInfo.info.name}, result:${popInfo.result}\n`
}
}, {
animated: true,
launchMode: LaunchMode.STANDARD
}).catch((error: Error) => {
this.message += "pushDestination err\n"
}).then(() => {
this.message += "pushDestination ok\n"
});
})
Button('pushDestinationByName').onClick(() => {
this.navPathStack.pushDestinationByName(
'navigationDemo2_page1',
"abc",
(popInfo: PopInfo) => {
this.message += `onPop ${popInfo.info.name} ${popInfo.result}\n`
},
true
).catch((error: Error) => {
this.message += "pushDestinationByName err\n"
}).then(() => {
this.message += "pushDestinationByName ok\n"
});
})
Scroll() {
Text(this.message)
}
.layoutWeight(1)
}
}
.titleMode(NavigationTitleMode.Mini)
.title("NavigationDemo_MySample1")
}
}
@Component
struct MySample2 {
navPathStack: NavPathStack = new NavPathStack()
build() {
Navigation(this.navPathStack) {
Column({space:10}) {
/*
* 本例用于演示导航的拦截,请参见 component/navigation/pages/NavigationDemo2_Page2.ets
*/
Button('pushPath').onClick(() => {
this.navPathStack.pushPath({
name: 'navigationDemo2_page2',
})
})
}
}
.titleMode(NavigationTitleMode.Mini)
.title("NavigationDemo_MySample2")
}
}
@Component
struct MySample3 {
navPathStack: NavPathStack = new NavPathStack()
@State message: string = ""
build() {
Navigation(this.navPathStack) {
Column({space:10}) {
/*
* 本例用于演示入栈的模式,请参见 component/navigation/pages/NavigationDemo2_Page3.ets
*/
Button('pushPath').onClick(() => {
this.navPathStack.pushPath({
name: 'navigationDemo2_page3',
}, {
launchMode: LaunchMode.STANDARD
})
})
}
}
.titleMode(NavigationTitleMode.Mini)
.title("NavigationDemo_MySample3")
}
}
pages\component\navigation\pages\NavigationDemo2_Page1.ets
/*
* NavDestination - 导航组件(Navigation)导航到的目标页
*/
import { LengthMetrics } from '@kit.ArkUI'
// 路由表中 routerMap 的 buildFunction 指定的 @Builder 函数,用于构造此路由指向的组件
// name - 路由名称
// param - 传递过来的数据
@Builder function navigationDemo2_page1_builder(name: string, param: Object) {
NavigationDemo2_Page1({
message: `路由名称:${name}, 参数:${param}\n\n`
})
}
@Component
struct NavigationDemo2_Page1 {
/*
* NavPathStack - 导航栈
* replacePath() - 栈顶出栈,然后将指定的页面入栈
* info - 入栈页面的信息(一个 NavPathInfo 对象,关于 NavPathInfo 请参见 NavigationDemo2.ets 中的说明)
* options - 选项(一个 NavigationOptions 对象)
* animated - 是否支持转场动画
* launchMode - 页面入栈的模式
* replacePathByName(name: string, param: Object, animated?: boolean) - 栈顶出栈,然后将指定的页面入栈
* removeByName() - 让指定名称的所有页面出栈
* removeByIndexes() - 让指定的所有位置的页面出栈
* removeByNavDestinationId() - 让指定 navDestinationId 的页面出栈
* pop() - 出栈并传回数据(传回了数据则会触发入栈时的 onPop 回调,否则不触发)
* popToName() - 路由回退到从栈底开始的第一个指定名称的页面
* popToIndex() - 路由回退到指定索引位置的页面
* moveToTop() - 将从栈底开始的第一个指定名称的页面移动到栈顶
* moveIndexToTop() - 将指定索引位置的页面移动到栈顶
* clear() - 清理当前导航栈的全部页面
* size() - 导航栈中所有 NavDestination 的数量
* getAllPathName() - 导航栈中所有 NavDestination 的名称
* getParamByIndex() - 导航栈中指定索引位置的 NavDestination 的参数信息(入栈时传递过来的数据)
* getParamByName() - 导航栈中指定名称的所有 NavDestination 的参数信息(入栈时传递过来的数据)
* getIndexByName() - 导航栈中指定名称的所有 NavDestination 的索引位置
*
* 注:可以认为导航栈中的每个页面都是一个 NavDestination 组件(导航栈中不包含 Navigation 组件)
*/
navPathStack: NavPathStack = new NavPathStack()
navDestinationId: string = "0"
@State message: string = ""
aboutToAppear(): void {
/*
* queryNavigationInfo() - 组件的内置方法,可以获取当前的 NavigationInfo
* navigationId - 当前 Navigation 的标识(由系统自动生成)
* pathStack - 当前 Navigation 的 NavPathStack 对象
*/
let navigationInfo = this.queryNavigationInfo();
this.message += `navigationInfo: ${JSON.stringify(navigationInfo)}\n\n`
}
build() {
/*
* NavDestination - 导航组件(Navigation)导航到的目标页
* onReady() - 当 NavDestination 构造子组件之前时的回调(回调参数是一个 NavDestinationContext 对象)
* pathInfo - 页面的信息(一个 NavPathInfo 对象,关于 NavPathInfo 请参见 NavigationDemo2.ets 中的说明)
* pathStack - 当前的导航栈(一个 NavPathStack 对象)
* navDestinationId - 当前 NavDestination 的标识(由系统自动生成)
*/
NavDestination() {
Column({space:10}) {
Flex({ wrap: FlexWrap.Wrap, justifyContent: FlexAlign.SpaceAround, space: { cross: LengthMetrics.vp(10) } }) {
Button('pushPath').onClick(() => {
this.navPathStack.pushPath({
name: 'navigationDemo2_page1',
param: "abc",
onPop: (popInfo: PopInfo) => {
this.message += `onPop name:${popInfo.info.name}, result:${popInfo.result}\n\n`
}
})
})
Button('replacePath').onClick(() => {
this.navPathStack.replacePath({
name: 'navigationDemo2_page1',
param: "abc",
onPop: (popInfo: PopInfo) => {
this.message += `onPop name:${popInfo.info.name}, result:${popInfo.result}\n\n`
}
}, {
animated: true,
launchMode: LaunchMode.STANDARD
})
})
Button('replacePathByName').onClick(() => {
this.navPathStack.replacePathByName('navigationDemo2_page1', "abc", true)
})
Button('removeByName').onClick(() => {
this.navPathStack.removeByName('navigationDemo2_page1')
})
Button('removeByIndexes').onClick(() => {
this.navPathStack.removeByIndexes([this.navPathStack.size() - 1])
})
Button('removeByNavDestinationId').onClick(() => {
this.navPathStack.removeByNavDestinationId(this.navDestinationId)
})
Button('pop').onClick(() => {
// 出栈并传回数据,会触发入栈时的 onPop 回调
// 第 1 个参数是传回的数据
// 第 2 个参数用于说明是否启用转场动画
// 返回值为出栈页面的信息(一个 NavPathInfo 对象,关于 NavPathInfo 请参见 NavigationDemo2.ets 中的说明)
let info = this.navPathStack.pop("xyz", true);
// 出栈但是不传回数据,不会触发入栈时的 onPop 回调
// let info = this.navPathStack.pop(true);
})
Button('popToName').onClick(() => {
// 路由回退到从栈底开始的第一个名为 navigationDemo2_page1 的页面
// 第 1 个参数是指定的页面的名称
// 第 2 个参数是传回的数据
// 第 3 个参数用于说明是否启用转场动画
// 返回值为从栈底开始的第一个名为 navigationDemo2_page1 的页面的索引位置,没有的话则返回 -1
let index = this.navPathStack.popToName("navigationDemo2_page1", "xyz", true);
})
Button('popToIndex').onClick(() => {
// 路由回退到索引位置为 0 的页面
// 第 1 个参数是指定的页面的索引位置
// 第 2 个参数是传回的数据
// 第 3 个参数用于说明是否启用转场动画
this.navPathStack.popToIndex(0, "xyz", true);
})
Button('moveToTop').onClick(() => {
// 将从栈底开始的第一个名为 navigationDemo2_page1 的页面移动到栈顶
// 第 1 个参数是指定的页面的名称
// 第 2 个参数用于说明是否启用转场动画
// 返回值为从栈底开始的第一个名为 navigationDemo2_page1 的页面的索引位置,没有的话则返回 -1
let index = this.navPathStack.moveToTop("navigationDemo2_page1", true);
})
Button('moveIndexToTop').onClick(() => {
// 将索引位置为 0 的页面移动到栈顶
// 第 1 个参数是指定的页面的索引位置
// 第 2 个参数用于说明是否启用转场动画
this.navPathStack.moveIndexToTop(0, true);
})
Button('clear').onClick(() => {
// 清理当前导航栈的全部页面
// 第 1 个参数用于说明是否启用转场动画
this.navPathStack.clear(true)
})
}
Scroll() {
Text(this.message)
}
.layoutWeight(1)
}
}
.title('NavigationDemo2_Page1')
.onReady((context: NavDestinationContext) => {
this.navPathStack = context.pathStack
this.navDestinationId = `${context.navDestinationId}`
this.message += `navDestinationId:${context.navDestinationId}\n\n`
this.message += `路由名称:${context.pathInfo.name}, 参数:${context.pathInfo.param}\n\n`
this.message += `导航栈中所有 NavDestination 的数量:\n${this.navPathStack.size()}\n\n`
this.message += `导航栈中所有 NavDestination 的名称:\n${JSON.stringify(this.navPathStack.getAllPathName())}\n\n`
this.message += `导航栈中第 1 个 NavDestination 的参数信息:\n${JSON.stringify(this.navPathStack.getParamByIndex(0))}\n\n`
this.message += `导航栈中指定名称的所有 NavDestination 的参数信息:\n${JSON.stringify(this.navPathStack.getParamByName("navigationDemo2_page1"))}\n\n`
this.message += `导航栈中指定名称的所有 NavDestination 的索引位置:\n${JSON.stringify(this.navPathStack.getIndexByName("navigationDemo2_page1"))}\n\n`
})
}
}
pages\component\navigation\pages\NavigationDemo2_Page2.ets
/*
* NavDestination - 导航组件(Navigation)导航到的目标页
*/
import { promptAction } from '@kit.ArkUI'
// 路由表中 routerMap 的 buildFunction 指定的 @Builder 函数,用于构造此路由指向的组件
// name - 路由名称
// param - 传递过来的数据
@Builder function navigationDemo2_page2_builder(name: string, param: Object) {
NavigationDemo2_Page2()
}
@Component
struct NavigationDemo2_Page2 {
/*
* NavPathStack - 导航栈
* onReady() - 当 NavDestination 构造子组件之前时的回调(回调参数是一个 NavDestinationContext 对象)
* pathInfo - 页面的信息(一个 NavPathInfo 对象,关于 NavPathInfo 请参见 NavigationDemo2.ets 中的说明)
* pathStack - 当前的导航栈(一个 NavPathStack 对象)
* navDestinationId - 当前 NavDestination 的标识(由系统自动生成)
* setInterception() - 设置当前导航栈的拦截行为(指定一个 NavigationInterception 对象)
* willShow(), didShow() - 页面跳转前和跳转后的回调
* from - 页面跳转之前的栈顶页面信息(一个 NavDestinationContext 对象),如果此值为 "navBar" 则表示跳转前的页为 Navigation 组件
* to - 页面跳转之后的栈顶页面信息(一个 NavDestinationContext 对象),如果此值为 "navBar" 则表示跳转的目标页为 Navigation 组件
* operation - 页面跳转的类型(NavigationOperation 枚举)
* PUSH - 此次转场为页面进场
* POP - 此次转场为页面退场
* REPLACE - 此次转场为页面替换
* isAnimated - 页面跳转是否启用了转场动画
* modeChange() - 导航组件的显示模式发生变化时的回调(关于导航组件的显示模式请参见 NavigationDemo.ets 中的说明)
* onBackPressed() - 拦截返回行为
* return false 则使用默认的返回行为
* return true 则使用自定义的返回行为
*/
navPathStack: NavPathStack = new NavPathStack()
@State message: string = ""
build() {
NavDestination() {
Column({space:10}) {
Button('pushPath').onClick(() => {
this.navPathStack.pushPath({
name: 'navigationDemo2_page2',
})
})
Scroll() {
Text(this.message)
}
.layoutWeight(1)
}
}
.title('NavigationDemo2_Page2')
.onBackPressed(() => {
// 自定义返回行为
promptAction.showDialog({
title: '确认返回码?',
buttons: [ { text: '确认', color: '#ff0000' }, ]
}, (err, data) => {
if (!err) {
this.navPathStack.pop()
}
});
return true
})
.onReady((context: NavDestinationContext) => {
this.navPathStack = context.pathStack;
this.navPathStack.setInterception({
// 页面跳转前拦截
willShow: (from: NavDestinationContext | "navBar", to: NavDestinationContext | "navBar", operation: NavigationOperation, isAnimated: boolean) => {
this.message += `willShow from:${JSON.stringify(from)}\n\n`
this.message += `willShow to:${JSON.stringify(to)}\n\n`
this.message += `willShow operation:${JSON.stringify(operation)}\n\n`
this.message += `willShow animated:${JSON.stringify(isAnimated)}\n\n`
},
// 页面跳转后回调
didShow: (from: NavDestinationContext | "navBar", to: NavDestinationContext | "navBar", operation: NavigationOperation, isAnimated: boolean) => {
this.message += `didShow from:${JSON.stringify(from)}\n\n`
this.message += `didShow to:${JSON.stringify(to)}\n\n`
this.message += `didShow operation:${JSON.stringify(operation)}\n\n`
this.message += `didShow animated:${JSON.stringify(isAnimated)}\n\n`
},
// 导航组件的显示模式发生变化时的回调
modeChange: (mode: NavigationMode) => {
this.message += `modeChange mode:${JSON.stringify(mode)}\n\n`
}
})
})
}
}
pages\component\navigation\pages\NavigationDemo2_Page3.ets
/*
* NavDestination - 导航组件(Navigation)导航到的目标页
*/
// 路由表中 routerMap 的 buildFunction 指定的 @Builder 函数,用于构造此路由指向的组件
// name - 路由名称
// param - 传递过来的数据
@Builder function navigationDemo2_page3_builder(name: string, param: Object) {
NavigationDemo2_Page3()
}
@Component
struct NavigationDemo2_Page3 {
/*
* NavPathStack - 导航栈
* pushPath() - 将指定的页面入栈
* info - 入栈页面的信息(一个 NavPathInfo 对象)
* options - 选项(一个 NavigationOptions 对象)
* animated - 是否启用转场动画
* launchMode - 页面入栈的模式(LaunchMode 枚举)
* STANDARD - 标准的入栈
* MOVE_TO_TOP_SINGLETON - 从栈底向栈顶查找,如果存在相同名称的页面,则把这个相同名称的页面移动到栈顶,否则行为和 STANDARD 一样
* POP_TO_SINGLETON - 从栈底向栈顶查找,如果存在相同名称的页面,则路由会回退到这个相同名称的页面,否则行为和 STANDARD 一样
* NEW_INSTANCE - 保证使用新的页面实例入栈
*/
navPathStack: NavPathStack = new NavPathStack()
@State message: string = ""
build() {
NavDestination() {
Column({space:10}) {
Text(this.message)
Button('LaunchMode.STANDARD').onClick(() => {
this.navPathStack.pushPath({
name: 'navigationDemo2_page3',
}, {
launchMode: LaunchMode.STANDARD
})
})
Button('LaunchMode.MOVE_TO_TOP_SINGLETON').onClick(() => {
this.navPathStack.pushPath({
name: 'navigationDemo2_page3',
}, {
launchMode: LaunchMode.MOVE_TO_TOP_SINGLETON
})
})
Button('LaunchMode.POP_TO_SINGLETON').onClick(() => {
this.navPathStack.pushPath({
name: 'navigationDemo2_page3',
}, {
launchMode: LaunchMode.POP_TO_SINGLETON
})
})
Button('LaunchMode.NEW_INSTANCE').onClick(() => {
// 连续操作导航栈时,会忽略中间过程,只显示最终的结果
// 比如:
// 1、先 pop 再通过 STANDARD 方式 push 相同名称的页面的话,系统会认为操作前后的结果一致从而不做任何处理
// 2、先 pop 再通过 NEW_INSTANCE 方式 push 相同名称的页面的话,系统会先 pop 页面,然后再 push 一个新的页面实例
this.navPathStack.pop()
this.navPathStack.pushPath({
name: 'navigationDemo2_page3',
}, {
launchMode: LaunchMode.NEW_INSTANCE
})
})
}
}
.title('NavigationDemo2_Page3')
.onReady((context: NavDestinationContext) => {
this.navPathStack = context.pathStack;
this.message = `navDestinationId:${context.navDestinationId}`
})
}
}