HamronyOS开发5.0【路由容器之Navigation】 全面解析

什么是Navigation?

Navigation是ArkUI中的路由容器组件,一般作为首页的根容器,适用于模块内和跨模块的路由切换。支持一次开发,多端部署场景。

Navigation组件适用于模块内页面切换,通过组件级路由能力实现更加自然流畅的转场体验,是官方推荐的导航组件(相较于Router,Navigation的支持面更广,也更适合实现一多,所以建议使用Navigation)。

看到这里,很多iOS开发者可能比较熟悉,这个组件类似于iOS中的UINavigationController,可以用于根控制器实现页面的跳转和传值,同时可以实现系统或自定义标题栏、导航Item等,我们可以在Navigation中看到这些特性。

Navigation的组成

Navigation包含导航页(NavBar)和子页(NavDestination)。 导航页又包含以下三个部分:

  • 标题栏(Titlebar,包含菜单栏menu):通过title属性设置标题。
  • 内容区(Navigation子组件):默认首页显示导航内容(Navigation的子组件)或非首页显示(NavDestination的子组件),首页和非首页通过路由进行切换。
  • 工具栏(Toolbar):通过toolbarConfiguration实现对工具栏的配置。

1

上图所示为单页面模式布局示意图,左边为导航页,右边为子页,可以通过路由转换实现页面跳转。

  1. 标题栏

2

  1. 菜单栏

3

  1. 工具栏

4

路由跳转

5

Navigation路由跳转相关操作是基于页面栈NavPathStack提供的方法进行,每个Navigation都需要创建并传入一个NavPathStack对象,用于管理页面。

NavPathStack通过push()pop()remove()等操作控制页面的入栈和出栈,从而完成页面的切换。

路由跳转的方式:

1. 跳转

  • 通过页面的name去跳转,并可以携带参数;
this.pageStack.pushPath({ name: "PageOne", param: "PageOne Param" })
this.pageStack.pushPathByName("PageOne", "PageOne Param")
  • 带返回回调的跳转,跳转时添加onPop回调,能在页面出栈时获取返回信息,并进行处理;
this.pageStack.pushPathByName('PageOne', "PageOne Param", (popInfo) => {
    console.log('Pop page name is: ' + popInfo.info.name + ', result: ' + JSON.stringify(popInfo.result))
});
  • 带错误码的跳转,跳转结束会触发异步回调,返回错误码信息
this.pageStack.pushDestinationByName('PageOne', "PageOne Param")
.catch((error: BusinessError) => {
    console.error(`Push destination failed, error code = ${error.code}, error.message = ${error.message}.`);
}).then(() => {
    console.error('Push destination succeed.');
});

2. 返回

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

3. 替换

// 将栈顶页面替换为PageOne
this.pageStack.replacePath({ name: "PageOne", param: "PageOne Param" })
this.pageStack.replacePathByName("PageOne", "PageOne Param")

4. 删除

// 删除栈中name为PageOne的所有页面
this.pageStack.removeByName("PageOne")
// 删除指定索引的页面
this.pageStack.removeByIndexes([1,3,5])

5. 参数获取

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

6. 路由拦截

页面栈提供了setInterception方法用于页面拦截,在willShow回调中通过修改路由栈来实现路由拦截重定向的能力。

this.pageStack.setInterception({
  willShow: (from: 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 === 'PageTwo') {
      target.pathStack.pop();
      target.pathStack.pushPathByName('PageOne', null);
    }
  }
})

Demo实现

为了搞清楚跳转的细节,我们可以创建一个简单的Demo,实现跳转、返回、替换以及跳转到根页面。 Demo中有Index、Page0-Page2四个页面。

  • 在Index页面,实现跳转下一页功能;
import { BusinessError } from '@kit.BasicServicesKit';
import { PageUtils } from '../utils/PageUtils';
import { Page0 } from './Page0';
import { Page1 } from './Page1';
import { Page2 } from './Page2';

@Entry
@Component
struct Index {
  @State currentIndex: number = 0;
  private tabsController: TabsController = new TabsController();
  // 创建一个页面栈对象并传入Navigation
  @Provide('pageStack') pageStack: NavPathStack = new NavPathStack()

  @Builder
  pageMap(name: string) {
    if (name === 'Index') {
      Index()
    } else if(name === 'Page0') {
      Page0()
    } else if (name === 'Page1') {
      Page1()
    } else if (name === 'Page2') {
      Page2()
    }
  }
  
  build() {
    Navigation(this.pageStack) {
      Column() {
        Blank(50)
        Button("下一页")
          .onClick(() => {
            let pathInfo : NavPathInfo = new NavPathInfo('Page0', "A");
            this.pageStack.pushDestination(pathInfo);
          })
        Blank(50)
        Text(PageUtils.getPageStackInfo(this.pageStack))
      }
    }
    .title("Index")
    .navDestination(this.pageMap)

  }

}

首页使用Navigation组件并传入页面栈对象this.pageStack,其中,pageMap必须予以实现,否则无法正确跳转。在首页使用@Provide('pageStack') 创建一个页面栈对象并传入Navigation,方便其他页面接收。整个跳转与返回的过程由该页面栈控制。

6

  • 在Page0页面,实现跳转下一个和返回功能;
import { PageUtils } from '../utils/PageUtils';

@Component
export struct Page0 {
  @Consume('pageStack') pageStack: NavPathStack;

  build() {

    NavDestination() {
      Column() {
        Blank(50)
        Button("下一页")
          .onClick(() => {
            this.pageStack.pushPathByName('Page1', "B", (popInfo) => {
              console.log('Pop page name is: ' + popInfo.info.name + ', result: ' + JSON.stringify(popInfo.result))
            });
          })
        Blank(50)
        Button("返回")
          .onClick(() => {
            this.pageStack.pop("agree", true);
          })
        Blank(50)
        Text(PageUtils.getPageStackInfo(this.pageStack))
      }
    }
    .title('Page0')
  }
}

子页面需使用NavDestination组件。这里,我们通过@Consume('pageStack') pageStack: NavPathStack;获取首页创建的页面栈,用于操作页面跳转,这里的pageStack需要也创建时使用的key一致。

7

当然,这种获取页面栈的方式是有耦合的,我们还可以通过OnReady回调获取:

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

  build() {
    NavDestination() {
      ...
    }.title('PageOne')
    .onReady((context: NavDestinationContext) => {
      this.pathStack = context.pathStack
    })
  }
}

另外还可以通过全局AppStorage接口设置获取以及自定义组件查询接口获取。我们可以根据实际情况选取适合的方式。

  • 在Page1页面,实现跳转下一个、返回及页面替换功能;
import { PageUtils } from '../utils/PageUtils';

@Component
export struct Page1 {
  @Consume('pageStack') pageStack: NavPathStack;

  build() {
    NavDestination() {
      Column() {
        Blank(50)
        Button("下一页")
          .onClick(() => {
            this.pageStack.pushPathByName('Page2', "C", (popInfo) => {
              console.log('Pop page name is: ' + popInfo.info.name + ', result: ' + JSON.stringify(popInfo.result))
            });
          })
        Blank(50)
        Button("返回")
          .onClick(() => {
            this.pageStack.pop()
          })
        Blank(50)
        Button("替换 By name")
          .onClick(() => {
            this.pageStack.replacePathByName("Page0", "替换到Page0");
          })
        Blank(50)
        Text(PageUtils.getPageStackInfo(this.pageStack))
      }
    }
    .title('Page1')
  }
}

这样,我们就可以实现页面跳转。

8

  • 在Page2页面,实现返回及返回根页面功能。
import { PageUtils } from '../utils/PageUtils';

@Component
export struct Page2 {
  @Consume('pageStack') pageStack: NavPathStack;

  build() {
    NavDestination() {
      Column() {
        Blank(50)
        Button("返回")
          .onClick(() => {
            this.pageStack.pop()
          })
        Blank(50)
        Button("返回根视图")
          .onClick(() => {
            this.pageStack.clear()
          })
        Blank(50)
        Text(PageUtils.getPageStackInfo(this.pageStack))
      }
    }
    .title("Page2")
  }
}

其中,PageUtils是用于获取页面栈信息的工具类,其实现如下:

export class PageUtils {

  static getPageStackInfo(pageStack: NavPathStack): string {

    let res: string = "";
    let pathNames = pageStack.getAllPathName();
    for (let index = 0; index < pathNames.length; index++) {
      const pathName = pathNames[index];
      const pathIndex = pageStack.getIndexByName(pathName);

      res += `page name: ${pathName}, `;
      res += `index: ${pathIndex}, `;
      res += `param: ${pageStack.getParamByName(pathName)}`;
      res += "\n";
    }

    return res;
  }

}

通过pageStack.getParamByName()获取到参数信息。

10

运行效果示例:

11

为了了解页面栈信息,在Demo中分别打印了页面名称、index以及参数信息。 可以看出,index是从0随着push()递增,遵从先进后出原则,当使用replace()进行页面替换时,一个页面对应多个index;当调用clear()时,页面栈被清空,返回到根页面。

Navigation导航下的页面生命周期

Navigation作为路由容器,其生命周期承载在NavDestination组件上,以组件事件的形式开放。

其生命周期如下图所示:

13

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

14

最后,我们需要知道,官方推荐使用Navigation,原先使用Router的应用,建议尽快切换Navigation,避免由于页面的持续增加导致迁移工作量变大。

以上就是Navigation相关的一些基础内容,由于大篇幅不适合阅读与分享,后续会继续整理自定义路由、页面监听和Router切换Navigation等方面内容,希望能提供一些参考和思路引导。


最后呢,很多开发朋友不知道需要学习那些鸿蒙技术?鸿蒙开发岗位需要掌握那些核心技术点?为此鸿蒙的开发学习必须要系统性的进行。

而网上有关鸿蒙的开发资料非常的少,假如你想学好鸿蒙的应用开发与系统底层开发。你可以参考这份资料,少走很多弯路,节省没必要的麻烦。由两位前阿里高级研发工程师联合打造的《鸿蒙NEXT星河版OpenHarmony开发文档》里面内容包含了(ArkTS、ArkUI开发组件、Stage模型、多端部署、分布式应用开发、音频、视频、WebGL、OpenHarmony多媒体技术、Napi组件、OpenHarmony内核、Harmony南向开发、鸿蒙项目实战等等)鸿蒙(Harmony NEXT)技术知识点

如果你是一名Android、Java、前端等等开发人员,想要转入鸿蒙方向发展。可以直接领取这份资料辅助你的学习。下面是鸿蒙开发的学习路线图。

​​​​1

高清完整版请点击《鸿蒙NEXT星河版开发学习文档》

针对鸿蒙成长路线打造的鸿蒙学习文档。话不多说,我们直接看详细资料鸿蒙(OpenHarmony )学习手册(共计1236页)与鸿蒙(OpenHarmony )开发入门教学视频,帮助大家在技术的道路上更进一步。

《鸿蒙 (OpenHarmony)开发学习视频》

《鸿蒙生态应用开发V2.0白皮书》

《鸿蒙 (OpenHarmony)开发基础到实战手册》

《鸿蒙开发基础》

《鸿蒙开发进阶》

《鸿蒙开发实战》
在这里插入图片描述

获取这份鸿蒙星河版学习资料,请点击→《鸿蒙NEXT星河版开发学习文档》

总结

鸿蒙—作为国家主力推送的国产操作系统。部分的高校已经取消了安卓课程,从而开设鸿蒙课程;企业纷纷跟进启动了鸿蒙研发。

并且鸿蒙是完全具备无与伦比的机遇和潜力的;预计到年底将有 5,000 款的应用完成原生鸿蒙开发,未来将会支持 50 万款的应用。那么这么多的应用需要开发,也就意味着需要有更多的鸿蒙人才。鸿蒙开发工程师也将会迎来爆发式的增长,学习鸿蒙势在必行!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值