鸿蒙Harmony开发:路由框架方案-Navigation

 前言

官方文档中有两套路由方案: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回调          
}); 
    
  1. 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)
    
  1. 在子组件构建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<unknown>;

当重复进入一个页面的时候,每次传参都会累计,即使不传也会按不传入计入。做到页面进入次数与传参次数一一对应。举个例子:

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

也就是说:如果不传参数也会记录下来。

  1. 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
  1. 从B页面返回到A页面,即:A->B->A

执行顺序:

  • B onHidden
  • A onShown
  • B onDisAppear
  1. 从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因为还在显示中,先隐藏,再销毁。

  1. 将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页面。

  1. 将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:记录了修改了多少次,即使像弹出到第一个页面,它也会忠实的记录中间一共变更过几次,很适用于页面跳转统计次数。

  • 9
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值