头图

【高心星出品】

Navigation实现页面路由

Navigation:路由导航的根视图容器,一般作为页面(@Entry)的根容器去使用,包括单页面(stack)、分栏(split)和自适应(auto)三种显示模式。Navigation组件适用于模块内和跨模块的路由切换,通过组件级路由能力实现更加自然流畅的转场体验,并提供多种标题栏样式来呈现更好的标题和内容联动效果。一次开发,多端部署场景下,Navigation组件能够自动适配窗口显示大小,在窗口较大的场景下自动切换分栏展示效果。

在这里插入图片描述

  1. Title:标题栏,通过title属性对标题栏进行设置。通过menus配置菜单
  2. NavContent:内容区域,默认首页显示导航内容(Navigation的子组件)或非首页显示(NavDestination的子组件),首页和非首页通过路由进行切换。
  3. ToolBar:工具栏,通过toolbarConfiguration实现对工具栏的配置,如果不配置此属性,ToolBar不显示。竖屏最多支持显示5个图标,多余的图标会被放入自动生成的更多图标。
  4. NavDestination:作为子页面的根容器,用于显示Navigation的内容区。具备两种类型:STANDARD(标准类型,NavDestination的生命周期跟随NavPathStack栈中标准NavDestination变化而改变),DIALOG(默认透明。不影响其他NavDestination的生命周期)。
  5. NavPathStack:Navigation路由栈,由于管理NavDestination组件的路由跳转。推荐使用NavPathStack配合navDestination属性进行页面路由。

完整的Navigation

要求入口页面必须是Navigation组件作为容器,子页面必须是NavDestinnation组件作为容器。由于我们将页面内容区的页面都添加了页面栈,所以在子页面拿到页面栈之后也可以实现子页面之间跳转。

在这里插入图片描述

入口页面
import { page1 } from '../viewmodel/page1'
import { page2 } from '../viewmodel/page2'

@Entry
@Component
struct Index {
  // 菜单栏
  private menuitems:NavigationMenuItem[]=[
    {
      value:'菜单1',
      icon:'resources/base/media/startIcon.png',
      action:()=>{}
    },
    {
      value:'菜单2',
      icon:'resources/base/media/startIcon.png',
      action:()=>{}
    }
  ]
  // 工具栏
  private toolbaritems:ToolbarItem[]=[
    {
      value:'工具1',
      icon:$r('app.media.startIcon'),
      action:()=>{}
    },
    {
      value:'工具2',
      icon:$r('app.media.startIcon'),
      action:()=>{}
    },
    {
      value:'工具3',
      icon:$r('app.media.startIcon'),
      action:()=>{}
    },
    {
      value:'工具4',
      icon:$r('app.media.startIcon'),
      action:()=>{}
    }
  ]
  // 页面路由栈
  @Provide('pathstack') pathstack:NavPathStack=new NavPathStack()

  // 内容页
  @Builder
  pagemap(pagename:string){
   if(pagename=='page1'){
     page1()
   }else if(pagename=='page2'){
     page2()
   }
  }

  build() {
    Navigation(this.pathstack){
     Column({space:20}){
       Text('page1').fontSize(28).padding(10).border({width:2,color:Color.Red})
         .onClick(()=>{
           this.pathstack.pushPath({name:'page1'})
         })
         .width('100%')
       Text('page2').fontSize(28).padding(10).border({width:2,color:Color.Red})
         .onClick(()=>{
           this.pathstack.pushPath({name:'page2'})
         })
         .width('100%')
     }.width('100%')
      .height('100%')
      .backgroundColor(Color.Gray)
    }
    // 标题栏
    .title('主标题')
    // 菜单栏
    .menus(this.menuitems)
    // 工具栏
    .toolbarConfiguration(this.toolbaritems)
    // 内容页
    .navDestination(this.pagemap)



  }
}
子页面
/**
 *作者:gxx
 *时间:2024/10/24 10:13
 *功能:
 **/
@Component
export struct page1 {
//   子页面拿到页面栈
@Consume('pathstack') pathstack:NavPathStack
  private menuitems: NavigationMenuItem[] = [
    {
      value: '菜单1',
      icon: 'resources/base/media/startIcon.png',
      action: () => {
      }
    },
    {
      value: '菜单2',
      icon: 'resources/base/media/startIcon.png',
      action: () => {
      }
    }
  ]

  build() {
    NavDestination() {
      Column() {
        Text('page1').fontSize(28).fontWeight(FontWeight.Bolder)
      }.width('100%')
      .height('100%')
      .justifyContent(FlexAlign.Center)

    }.title('页面标题')
    .menus(this.menuitems)

  }
}

页面跳转

普通页面跳转

pushpath和replacepath都可以实现页面跳转,他们的区别就是在页面栈中覆盖页面还是替换页面的区别,replacepath无法实现pop回调,因为当前页在跳转的时候已经被销毁。

// 子页面之间也可以跳转
this.pathstack.replacePath({name:'page1',onPop:(info)=>{console.log('gxxt ',JSON.stringify(info))}})
this.pathstack.pushPath({name:'page1',onPop:(info)=>{console.log('gxxt ',JSON.stringify(info))}})

这里面的onpop是可选参数,如果设置了意味着在目标页可以通过调用页面栈的pop方法回到当前页,并且将返回值带过来,相当于带返回值的跳转。

.onBackPressed(()=>{
      this.pathstack.pop(result)
      return true
    })

此处result为目标页返回给当前页的返回值。

跳转携带数据

跳转的时候可以携带数据,通过param可以设置这些数据,并在目标页中获取。

.onClick(()=>{
  this.pathstack.pushPath({name:'page1',param:'来自首页的信息'})
})

这里携带了字符串类型的数据作为param。

.onReady((cxt)=>{
 let param= cxt.pathInfo.param as string
  promptAction.showToast({message:param})
})

可以在子页面的NavDestination的onready中获取携带过来的数据。

路由拦截

路由跳转中可以进行拦截处理,常用于路由重定向。比如主页面跳转到page1的时候,携带的数据为page2,重定向到page2.

.onClick(()=>{
  this.pathstack.pushPath({name:'page1',param:'page2'})
})

在首页跳转到page1页面。

aboutToAppear(): void {
  // 设置拦截器
  this.pathstack.setInterception({
    willShow:(from:NavDestinationContext|NavBar,to:NavDestinationContext|NavBar)=> {
      // 回到主页面的时候to是navbar
      if (typeof to == 'string') {
        return
      } else {
        let cxt = to as NavDestinationContext
        // 如果跳转到page1 但是携带数据是page2 就跳转到page2
        if (cxt.pathInfo.name == 'page1') {
          if (cxt.pathInfo.param == 'page2') {
          //移除page1
            this.pathstack.pop()
            this.pathstack.pushPath({ name: 'page2',param:'pagex' })
          }
        }
      }
    }
  })
}

在主页定义了页面栈的拦截器,只要跳转的目标是page1并且携带了数据page2,就将page1移除,重定向到page2,就相当于从主页直接跳转到了page2.

其他的

在主页中可以设置标题为mini并关闭后退按钮,从而保持界面统一化。

.titleMode(NavigationTitleMode.Mini)
.hideBackButton(true)

在子页中可以在生命周期方法onReady中拿到当前子页的上下文环境context,里面包含了页面路由的相关信息。

.onReady((cxt)=>{
 let param= cxt.pathInfo.param as string
  promptAction.showToast({message:param})
})

在子页中点击后退图标或者按后退键都是执行了NavDestination的onBackpress生命周期函数,我们可以重写该函数。

.onBackPressed(()=>{
  this.pathstack.pop('gxx')
  return true
})

高心星
1 声望1 粉丝

华为开发者专家(HDE)。 10年教学经验,兼任多家科技公司技术顾问。先后从事JavaEE项目开发、Python爬虫、HarmonyOS移动应用开发等课程的教学工作。参与开发《鸿蒙应用开发基础》和《鸿蒙项目实战》等课程。