HarmonyOS基础:鸿蒙系统组件导航Navigation

大家好!我是黑臂麒麟(起名原因:一个出生全右臂自带纹身的高质量程序员😏),也是一位6+(约2个半坤年)的前端;
学习如像练武功一样,理论和实践要相结合,学一门只是也是一样;
这里会用两周的时间把所学的常用ArkUI基础的常用组件输出在网;
如需深究可前往高级ArkTS系列课程;
望对学习鸿蒙小伙伴有所帮助;

介绍

Navigation组件:是路由导航根视图容器,一般作为Page页面(@Entry修饰符)的component作为根容器使用。在应用开发中起到了模块内和跨模块(对于在想要跳转到的共享包HAR/HSP页面里)的路由切换。

作为前端工程师,使用惯了vue-router/react-router,在使用Navigation有一定的学习成本,但使用起来后会发现其用法和vue-router/react-router的用法很像。

Navgiation

介绍

这里实现组件跳转要利用到Navigation组件、NavDestination组件、NavPathStack来实现跳转,这里简单介绍:

  • Navigation路由导航根容器,包括标题栏、菜单栏、工具栏等。
  • NavDestination是Navigation子页面的根容器用于承载子页面的一些特殊属性以及生命周期等,结构属性上跟Navigation一样
  • NavPathStack管理路由容器

跳转

在使用navigation编写路由页面,一般navigation作为路由的根页面。在配合NavPathStack管理子路由NavDestination页面

  private arr: number[] = [1, 2, 3];
  @Provide('pageInfos') pageInfos: NavPathStack = new NavPathStack()
  private TooTmpItem: NavigationMenuItem = {'value': "", 'icon': "resources/base/media/ic_01_on.svg", 'action': ()=> {}}
  @State menuItems: Array<NavigationMenuItem> = [
    {
      value: 'menuItem1',
      icon: 'resources/base/media/ic_01_on.svg', // 图标资源路径
      action: () => {}
    }
  ]

  // 根据name,跳转相应的子路由页面
  @Builder
  pageMap(name: string){
    if (name === 'NavDestinationTitle1'){
      pageOneTmp()
    }else if(name === 'NavDestinationTitle2'){
      pageTweTmp()
    }else if(name === 'NavDestinationTitle3'){
      pageThreeTmp()
    }
  }
  build() {
    Column(){
      Navigation(this.pageInfos){
        TextInput({ placeholder: 'search...' })
          ...
        List({ space: 12 }){
          ForEach(this.arr, (item: number)=> {
            ListItem() {
              Text("NavRouter" + item)
                ...
                .onClick(() => {
                  this.pageInfos.pushPath({name: "NavDestinationTitle" + item})
                })
            }
          }, (item: number) => item.toString())
        }
        ...
      }
      .title("根路由页面") // navigation的标题
      .titleMode(NavigationTitleMode.Mini) // 标题模式 可选值:Mini、Normal、Full
      .mode(NavigationMode.Stack) // navigation页面显示模式:Stack(非折叠屏手机模式/正常显示屏幕)、Split(折叠屏手机/屏幕较大)、auto(自动判断)
      .menus([this.TooTmpItem, this.TooTmpItem, this.TooTmpItem]) // 导航栏菜单按钮
      .navDestination(this.pageMap) // 路由容器
      .toolbarConfiguration([this.TooTmp, this.TooTmp, this.TooTmp]) // tabbar的配置
    }
  }

@Component
export struct pageOneTmp {
  @Consume('pageInfos') pageInfos: NavPathStack;
  build() {
    NavDestination(){
      Column(){
        Text('NavDestinationContent1')
      }
      .width('100%')
      .height('100%')
    }
    .title('子页面1')
    .onBackPressed(() => {
      const popDestinationInfo = this.pageInfos.pop() // 弹出路由栈栈顶元素
      console.log('pop' + '返回值' + JSON.stringify(popDestinationInfo))
      return true
    })
  }
}


@Component
export struct pageTweTmp {
  @Consume('pageInfos') pageInfos: NavPathStack;
  build() {
    NavDestination(){
      Column(){
        Text('NavDestinationContent2')
      }
      .width('100%')
      .height('100%')
    }
    .title("子页面2")
    .onBackPressed(() => {
      const popDestinationInfo = this.pageInfos.pop() // 弹出路由栈栈顶元素
      console.log('pop' + '返回值' + JSON.stringify(popDestinationInfo))
      return true
    })
  }
}

在Navigtion的属性navDestination,我们传入路由容器,根据name不同,渲染不同的页面。
请添加图片描述

子页面之间跳转

介绍子页面跳转之前会用到系统路由表,我们先介绍:

  1. 在跳转目标模块的配置文件module.json5添加路由表配置:
{
  "module" : {
    "routerMap": "$profile:route_map"
  }
}
  1. 添加完路由配置文件地址后,需要在工程resources/base/profile中创建route_map.json文件。添加如下配置信息:
{
  "routerMap": [
    {
      "name": "PageOne", // 跳转页面名称。
      "pageSourceFile": "src/main/ets/pages/PageOne.ets", // 跳转目标页在包内的路径,相对src目录的相对路径。
      "buildFunction": "PageOneBuilder", // 	跳转目标页的入口函数名称,必须以@Builder修饰。
      "data": { // 应用自定义字段。可以通过配置项读取接口getConfigInRouteMap获取。
        "description" : "this is PageOne"
      }
    }
  ]
}
  1. 在跳转目标页面中,需要配置入口Builder函数,函数名称需要和route_map.json配置文件中的buildFunction保持一致,否则在编译时会报错。
// 跳转页面入口函数
@Builder
export function PageOneBuilder() {
  PageOne()
}

@Component
struct PageOne {
  pathStack: NavPathStack = new NavPathStack()

  build() {
    NavDestination() {
    }
    .title('PageOne')
    .onReady((context: NavDestinationContext) => {
        this.pathStack = context.pathStack
    })
  }
}
  1. 通过pushPathByName等路由接口进行页面跳转。(注意:此时Navigation中可以不用配置navDestination属性)。
@Entry
@Component
struct Index {
pageStack : NavPathStack = new NavPathStack();

build() {
  Navigation(this.pageStack){
  }.onAppear(() => {
    this.pageStack.pushPathByName("PageOne", null, false);
  })
  .hideNavBar(true)
}
}

上面就是配置系统路由表,然后就能实现子页面之间的跳转,下面是完整代码。

// PageOne.ets
@Builder
export function PageOneBuilder(name: string, param: Object){
  PageOne()
}

@Component
export struct PageOne {
  pageInfos: NavPathStack = new NavPathStack()

  build() {
    NavDestination(){
      Column() {
        Button('pushPathByName', { stateEffect: true, type: ButtonType.Capsule })
          .onClick(() => {
            let tmp = new TmpClass();
            this.pageInfos.pushPathByName('pageTwo', tmp)
          })

      }.width('100%').height('100%')
    }
    .title('pageOne')
    .onReady((context: NavDestinationContext) => {
      this.pageInfos = context.pathStack;
    })
  }
}
PageTwo.ets
@Builder
export function PageTwoBuilder(name: string, param: Object){
  PageTwo()
}
@Component
export struct PageTwo{
  pathStack: NavPathStack = new NavPathStack();

  build() {
    NavDestination() {
      Column(){
        Button('pushPathByName', { stateEffect: true, type: ButtonType.Capsule })
          .onClick(() => {
            console.info("1231231")
            this.pathStack.pushPathByName('pageOne', null)
          })
      }.width('100%').height('100%')
    }
    .title('pageTwo')
    .onReady((context: NavDestinationContext) => {
      this.pathStack = context.pathStack;
      console.log('current page config info is' + JSON.stringify(context.getConfigInRouteMap()))
    })
  }
}

在我们配置了系统路由后,上面pageOne和pageTwo子页面我们定义NavPathStack路由栈,然后在onReady中获取到路由栈,然后在利用pushPtahName的方法进行页面跳转。

拦截

NavPathStack提供了setInterception方法,用于设置Navigation页面跳转拦截回调。该方法需要传入一个NavigationInterception对象,该对象包含三个回调函数:

this.pageInfos.setInterception({
  // 页面跳转前回调,允许操作栈,在当前跳转生效。
  willShow: (form: NavDestinationContext | "navBar", to: NavDestinationContext | "navBar", operation: NavigationOperation, animated: boolean) => {
    if (typeof to === "string") {
      console.log("target page is navigation home page.");
      return
    }
    // 将跳转PageTwo的路由重定向PageOne
    let target: NavDestinationContext = to as NavDestinationContext;
    if (target.pathInfo.name === "NavDestinationTitle2"){
      target.pathStack.pop();
      target.pathStack.pushPath({name: "NavDestinationTitle3"}, false)
    }
  },
  // 页面跳转后回调,在该回调中操作栈会在下一次跳转生效。
  didShow: (){

  },
  // Navigation单双栏显示状态发生变更时触发该回调。
  modeChange: () {

  }
})

参数获取

NavPathStack通过Get相关接口去获取页面的一些参数。

// 获取栈中所有页面name集合
this.pageStack.getAllPathName()
// 获取索引为1的页面参数
this.pageStack.getParamByIndex(1)
// 获取PageOne页面的参数
this.pageStack.getParamByName("PageOne")
// 获取PageOne页面的索引集合
this.pageStack.getIndexByName("PageOne")

页面返回

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

// 返回到上一页
this.pageStack.pop()
// 返回到上一个PageOne页面
this.pageStack.popToName("PageOne")
// 返回到索引为1的页面
this.pageStack.popToIndex(1)
// 返回到根首页(清除栈中所有页面)
this.pageStack.clear()

子页面

页面生命周期

子页面里比较重要的概念就是生命周期,其生命周期大致可分为三类,自定义组件生命周期、通用组件生命周期和自有生命周期。其中,aboutToAppear和aboutToDisappear是自定义组件的生命周期(NavDestination外层包含的自定义组件),OnAppear和OnDisappear是组件的通用生命周期。剩下的六个生命周期为NavDestination独有。
生命周期时序如下图所示:
在这里插入图片描述

  • aboutToAppear:在创建自定义组件后,执行其build()函数之前执行(NavDestination创建之前),允许在该方法中改变状态变量,更改将在后续执行build()函数中生效。
  • onWillAppear:NavDestination创建后,挂载到组件树之前执行,在该方法中更改状态变量会在当前帧显示生效。
  • onAppear:通用生命周期事件,NavDestination组件挂载到组件树时执行。
  • onWillShow:NavDestination组件布局显示之前执行,此时页面不可见(应用切换到前台不会触发)。
  • onShown:NavDestination组件布局显示之后执行,此时页面已完成布局。
  • onWillHide:NavDestination组件触发隐藏之前执行(应用切换到后台不会触发)。
  • onHidden:NavDestination组件触发隐藏后执行(非栈顶页面push进栈,栈顶页面pop出栈或应用切换到后台)。
  • onWillDisappear:NavDestination组件即将销毁之前执行,如果有转场动画,会在动画前触发(栈顶页面pop出栈)。
  • onDisappear:通用生命周期事件,NavDestination组件从组件树上卸载销毁时执行。
  • aboutToDisappear:自定义组件析构销毁之前执行,不允许在该方法中改变状态变量。

结语

本篇文章的内容结束了。文章有不对或不完整的地方,望多指点;
望更多小伙伴们加入harmonyOS开发大家庭,壮大生态圈,让鸿蒙更好,让国产手机(物联网)系统更强大。
如对你学习有所帮助,希望可爱你动动小手,关注、点赞、收藏;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值