【鸿蒙开发教程】HarmonyOS Next 页面路由
即 Navigation + NavPathStack + navDestination// 主页用Navigation// 子页面用 NavDestination})有很多小伙伴不知道该从哪里开始学习鸿蒙开发技术?也不知道鸿蒙开发的知识点重点掌握的又有哪些?自学时频繁踩坑,导致浪费大量时间。结果还是一知半解。所以有一份实用的鸿蒙(HarmonyOS NEXT)全栈开发资料用来跟着学习是非常有必要
前言
根据研究机构Counterpoint Research发布的最新数据,2024年第一季度,鸿蒙OS份额由去年一季度的8%上涨至17%,iOS份额则从20%下降至16%。
这意味着,华为鸿蒙OS在中国市场的份额超越苹果iOS,已成中国第二大操作系统
随着鸿蒙市场份额的不断提升,相应的岗位也会迎来一个爆发式的增长。这对于想要换赛道的程序员来说是一个非常好的消息,话说大家最近有想法转型鸿蒙开发吗?
这里来跟大家聊一聊鸿蒙开发中的页面路由
官方文档中有两套路由方案:Router 和 Navigation。如果你刚开始入门,请直接看Navigation,并以此作为app的路由框架方案。
Naviagtion 的两种用法:底部带导航条,页面间跳转
底部带导航条
代码:
Navigation() {
...
// 内部布局样式
}
.title(this.NavigationTitle) // 自定义title布局样式 @builder
.menus(this.NavigationMenus) // 自定义顶部工具菜单样式 @builder
.titleMode(NavigationTitleMode.Full) // 固定大标题模式,Mini为小标题模式,还有Free模式
.toolbarConfiguration([ // 底部导航栏
{
value: $r("app.string.navigation_toolbar_add"),
icon: $r("app.media.ic_public_highlightsed"),
activeIcon: ... // 选中时的Icon
action:{} // 点击事件,
status: ... ToolbarItemStatus // 可以让点击样式不变,我想没人没事会设置这个吧|||
},
{
value: $r("app.string.navigation_toolbar_app"),
icon: $r("app.media.ic_public_highlights")
},
{
value: $r("app.string.navigation_toolbar_collect"),
icon: $r("app.media.ic_public_highlights"),
}
],
// api11 增加了背景颜色控制
{
backgroundColor: ...
backgroundBlurStyle: ... // 高斯模糊 BlurStyle
}
)
.hideTitleBar(false)
.hideToolBar(false)
随着API版本升级,有些方法官方已经废弃,有些方法则更改了名字,使用时记得检查当前版本。比
如早期工具栏方法叫toolBar,最新的api10改名为toolbarConfigration。
工具栏其实就是底部导航栏,注意:
●最多展示5个图标,
●超过5个,显示4个,增加一个更多图标。
●不设置则自动不展示底部导航条
●点击事件对应action
最多展示5个图标
超过5个,展示4个+更多
点击更多,弹出选项
页面之间路由(重点介绍)
即 Navigation + NavPathStack + navDestination
// 主页用Navigation
Navigation(this.navPathStack)
.navDestination(/* @Builder */)
// 子页面用 NavDestination
NavDestination()
.onBackPressed(()=>{
return true
})
跳转页面 pushPath 与 pushDestination
pushPath 与 pushDestination 在入参形式方面是一致的,区别在于 pushPath 没有返回值,而 pushDestination 会返回一个异常结果 Promise 错误码与router路由错误码一致。
1.pushPath 与 pushPathByName
两者之间除了参数传递形式不同,入参含义和功能是一样。
●pushPath(info: NavPathInfo, animated?: boolean): void
其中,NavPathInfo: (name: string, param: unknown, onPop?: (PopInfo)=>void)
●pushPathByName(name: string, param: Object, onPop: (PopInfo)=>void, animated?: boolean): void
/**
* name 为页面的名字,对应 navDestination 的 @builder 方法内构建的键值对名字
* param 为传递的参数,object类型,
*/
this.navPathStack.pushPath({name: "pageTwo", param: new ParamWithOp(), onPop: (popInfo: PopInfo)=>{
// popInfo 是跳转页面后,返回的结果,类似android的onResult回调
}})
this.navPathStack.pushPathByName("pageTwo", new ParamWithOp(), (popInfo)=>{
// popInfo 是跳转页面后,返回的结果,类似android的onResult回调
});
2.pushDestination 与 pushDestinationByName
这个两个方法与pushPath那个两个方法相对应,区别就是会返回一个错误异常Promise
this.navPathStack.pushDestination({name: 'pageTwo', param: new ParamWithOp(), onPop: (popInfo: PopInfo)=>{
// popInfo 是跳转页面后,返回的结果,类似android的onResult回调
}}).catch((error: BusinessError)=>{
// 跳转失败,会返回错误码及错误信息
}).then(()=>{
// 跳转成功
});
实际工作中,pushDestination 要更实用一些,它可以返回跳转失败的错误内容,便于开发阶段调试。使用它的时候要注意:它是异步方法Promise。
补充:关于页面之间的参数数据可以传递多大,还不清楚,已提问鸿蒙论坛
替换页面 replacePath 与 replacePathByName
与pushPath方法调用类似,与之相比,缺少了一个异步方法。其他传递参数是一致的。另外,注意生命周期的变化,详见后文中的生命周期。
移动页面 moveToPop 与 moveIndexToPop
吐个槽:鸿蒙这个命名到这里就有点割裂了。moveToPop与之前的pushPathByName和replacePathByName相对应,通过name值来操作的。如此推下来,这个名字应该叫movePathToPopByName才合适。
这个操作就是将页面栈里有的放置到最顶部,这里有几点注意:
●当前调用者并没有被销毁
●页面栈的顺序被改变了,如A-B-C,C将A移动到顶端,那么当前页面栈的顺序是B,C,A。
●每个页面对应的位置索引值也发生了变更。移动前,B的index值是1,移动后B的index变为0,而A移动前,index值是0,移动后index值变为了2。
子组件获取 Navivagtion的 NavPathStack
在子组件中,NavPathStack很重要,页面跳转和传参都需要它:
1.返回上一个界面: navPathStack.pop()
2.获取上一个页面进入时传递的参数:navPathStack.getParamByName / getParamByIndex
3.继续跳转下一个页面:navPathStack.pushPath()
子组件获取 NavPathStack 的两种方式:
1.构建子组件视图的时候,将NavPathStack作为参数传进去,但 NavPathStack 要使用装饰器,如@Provide
@Provide('pageInfo') navPathStack: NavPathStack = new NavPathStack()
// navDestination(/* @Builder */) 中的@Builder参数
@Builder
PageMap(name: string) {
if (name === 'pageTwo') {
pageTwoTmp(this.navPathStack)
}
}
// 子组件
@Builder
export function pageTwoTmp(info: NavPathStack)
2.在子组件构建NavDestiation的时候,增加onReady方法,接收回调的时候获取NavPathStack。这样的话,父组件的NavPathStack就不用增加装饰器了,子组件也不用@Builder方式创建,而是直接用@Component + struct 方式编写。
@Component
struct PageOne {
private stack: NavPathStack | null = null;
private pathInfo: NavPathInfo | null = null;
build() {
NavDestination() {
...
}
.onReady((ctx: NavDestinationContext) => {
// 在NavDestination中能够拿到传来的NavPathInfo和当前所处的NavPathStack
try {
this.stack = ctx.pathStack;
this.pathInfo = ctx.pathInfo; // 这里面带着上一个界面传递过来的参数 params
} catch (e) {
console.log(`testTag onReady catch exception: ${JSON.stringify(e)}`)
}
})
}
}
就项目实际使用而言,第一种方式,子视图只能通过获取NavPathStack来获取页面间的参数传递。而第二种,即可以通过NavPathStack获取,也可以通过pathInfo来获取上一层页面传递过来的参数。
页面之间传递参数获取
即:A->B,B获取A传递的参数
通过 NavPathStack 获取
NatPathStack 提供了两种方式获取,getParamByName 和 getParamByIndex
1.getParamByName
通过页面名字获取参数,如果同名的进入多次,就会有多次参数传递,所以这里返回的是一个数组:
getParamByName(name: string): Array;
当重复进入一个页面的时候,每次传参都会累计,即使不传也会按不传入计入。做到页面进入次数与传参次数一一对应。举个例子:
A->A,即A页面反复跳转到自己,那么,按隔一次传一次参数的方式的话:
this.count += 1
this.navPathStack.pushPath({ name: "pageOne", param: (this.count % 2 === 0) ? this.count : undefined });
// 内部获取参数
this.stack.getParamByName('pageOne') // 这里会返回一个数组
其结果如下:
,2,4,6,8,10
也就是说:如果不传参数也会记录下来。
2.getParamByIndex
通过栈的位置获取参数,返回一个unknown:getParamByIndex(index: number): unknown | undefined;
这里说明一下,如果param不传,返回的是undefined,如果传了null,则返回null。
通过PathInfo获取
即调用PathInfo.param获取,只能获取页面进入时最新的参数。
注意:param不传,或者传递null,得到的值都是undefined。
PathNavStack获取是数据内容最全面,但需要自行处理好获取参数是哪一个,尤其是同一页面多次进入的情况。PathInfo虽然只能获取当前页面进来时的参数,但它避免了获取时出错的可能性。
返回参数
当页面返回时,可以携带参数给上一个页面。上一个页面通过跳转时注册的onPop监听返回值
返回时,调用NavPathStack的pop方法:pop(result: Object, animated?: boolean): NavPathInfo | undefined;
this.navPathStack.pop(/* 返回数据内容 */)
this.navPathStack.pushPath({name: 'page', onPop: (popInfo: PopInfo)=>{
// 接收返回值结果 popInfo.result
}});
注意:返回数据,并没有在NavPathStack中保存,不像跳转页面时的param,会被保存起来。
生命周期
对应的生命周期方法
.onReady()
.onAppear()
.onShown()
.onHidden()
.onDisAppear()
1.A页面启动B页面,即:A->B
执行的顺序是:
●A onReady
●A onAppear
●A onShown
●B onReady
●B onAppear
●A onHidden
●B onShown
2.从B页面返回到A页面,即:A->B->A
执行顺序:
●B onHidden
●A onShown
●B onDisAppear
3.B页面跳转到D页面,D页面直接返回到A页面,即:A->B->C->D->A
执行的对应方法是:this.navPathStack.popToName(“A”)
执行的顺序是:
●C onDisAppear
●B onDisAppear
●D onHidden
●A onShown
●D onDiaAppear
这里注意:B,C,D的销毁顺序,即先弹出C,再弹出B,D因为还在显示中,先隐藏,再销毁。
4.将B页面替换为A页面,即:A->B->A(replace)
执行的对应方法是:this.navPathStack.replacePath({name:“A”})
执行的顺序是:
●A onReady
●A onAppear
●B onHidden
●A onShown
●B onDisAppear
这里由于是替换操作replace,所以要创建A页面,然后再销毁B页面。当前栈中,会有两个A页面。
5.将A页面移动到最前,即:A->B->A(move)
执行的对应方法是:this.moveToPop(‘A’)
执行的顺序是:
●B onHidden
●A onShown
这里B并没被销毁,只是将A页面从栈中移动到了栈顶位置,做了一个栈内操作。
onBackPressed
onBackPressed,这里接收一个回调,监听返回键按钮,包括视图上的返回键,和手机物理键的返回键。这里回调返回false,则不拦截返回键按钮。如果返回true,则点击返回键,均不会执行返回操作。
.onBackPressed(()=>{
// 可以添加自己的代码,如返回去哪里,并带参数
this.navPathStack.pop() // 这里可以补充返回参数
return true // 返回true的话,则不让其执行返回操作
})
clear 方法
清空NavPathStack,意味着直接返回到最起始的 Navigation 那个页面
NavPathStack 中的参数
1.pathArray:记录了路由数据"pathArray":[{“name”:“PageA”,“param”:“1”,“index”:0}]
2.changeFlag:记录了修改了多少次,即使像弹出到第一个页面,它也会忠实的记录中间一共变更过几次,很适用于页面跳转统计次数。
写在最后
有很多小伙伴不知道该从哪里开始学习鸿蒙开发技术?也不知道鸿蒙开发的知识点重点掌握的又有哪些?自学时频繁踩坑,导致浪费大量时间。结果还是一知半解。所以有一份实用的鸿蒙(HarmonyOS NEXT)全栈开发资料用来跟着学习是非常有必要的。
这份鸿蒙(HarmonyOS NEXT)资料包含了鸿蒙开发必掌握的核心知识要点,内容包含了
最新鸿蒙全栈开发学习线路
鸿蒙HarmonyOS开发教学视频
大厂面试真题
鸿蒙OpenHarmony源码剖析
这份资料能帮住各位小伙伴理清自己的学习思路,更加快捷有效的掌握鸿蒙开发的各种知识。有需要的小伙伴自行领取,,先到先得~无套路领取!!
获取这份完整版高清学习路线,请点击→鸿蒙全栈开发学习资料
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)