openharmony 鸿蒙 arkts-navigation-jump

2026-08-25 浏览 (1)

Navigation页面路由

Navigation路由相关操作均基于导航控制器NavPathStack提供的方法实现,每个Navigation都需要创建并传入一个NavPathStack对象,用于管理页面。NavPathStack主要提供了页面跳转、页面返回、页面替换、页面删除、参数获取、路由拦截等功能。

在API version 9,Navigation需要配合NavRouter组件实现页面路由。从API version 10开始,更推荐使用NavPathStack实现页面路由。

路由相关的几个关键概念:

  • 路由表:定义了页面名称和NavDestination页面的映射,若跳转时传入的页面名称未在路由表里注册,会跳转失败。系统提供了系统路由表自定义路由表两种实现方式。
  • 路由栈:NavDestination页面以栈结构管理,每个Navigation都有自己的路由栈,不可共享。路由栈主要由NavPathStack控制,此外可通过NavPathStack.getPathStack获取完整路由栈信息。
  • 页面转场:页面跳转动画,Navigation默认提供了页面切换的转场动画,也支持开发者自定义转场动画。

说明:

  • NavPathStack对象和Navigation需要一一对应,不可复用。

  • NavPathStack主要控制的是NavDestination页面跳转、删除等,无法直接操作NavBar导航栏,若想跳转到NavBar页面只能通过clear清空路由栈的方式。

  • 不建议开发者通过监听生命周期的方式管理自己的路由栈,可通过NavPathStack.getPathStack查询路由栈。

  • 在应用处于后台状态下,调用NavPathStack的栈操作方法,会在应用再次回到前台状态时触发刷新。

创建导航页面

构建导航根容器

首先,开发者需要创建一个Navigation作为导航根容器,并创建一个NavPathStack对象作为构造入参传给Navigation组件,以实现二者的绑定,后续的路由操作均基于该NavPathStack展开。

@Entry
@Component
struct Index {
  // 创建一个导航控制器对象并传入Navigation
  pageStack: NavPathStack = new NavPathStack();
  // ...
  build() {
    Navigation(this.pageStack) {
      // ...
    }.title('Main')
  }
}

构建子页面

为每个NavDestination声明对外的实例化方法,如代码中的PageOneBuilder,执行该方法会创建一个PageOne的自定义组件,该组件就是一个Navigation的子页面。

@Builder
export function PageOneBuilder(name: string, param: string) {
  PageOne({ name: name, value: param });
}

@Component
export struct PageOne {
  navPathStack: NavPathStack = new NavPathStack();
  name: string = '';
  @State value: string = '';
  context = this.getUIContext().getHostContext();

  build() {
    NavDestination() {
      // ...
    }.title(`${this.name}`)
    // ...
  }
}

配置路由表

系统提供了系统路由表和自定义路由表两种实现方式,此处以系统路由表为例。将子页面中写好的实例化方法与它的name(开发者自定义)在路由表配置文件中注册,配置文件需要自行创建,路径:entry/src/main/resources/base/profile/router_map.json

{
  "routerMap": [
    {
      "name": "basicPageOne", // 路由页面的唯一标识符
      "pageSourceFile": "src/main/ets/pages/PageOne.ets", // 源码所在路径
      "buildFunction": "PageOneBuilder" // 页面的实例化方法名称
    }
  ]
}

创建好路由表后,需要将其注册到工程中,在工程配置文件module.json5中的module字段里配置"routerMap": "$profile:router_map"

路由操作

路由栈获取

Navigation根容器和子页面以及路由表配置完成后,即可通过调用NavPathStack的API来实现页面之间的跳转。上文提到在一个Navigation容器中,只有一个NavPathStack对象,那么在子页面里执行路由操作就需要获取此NavPathStack对象,有两种方式:

  • 方式一:使用AppStorage存储与获取。

    @Entry
    @Component
    struct NavigationPage {
      navStack: NavPathStack = new NavPathStack();
    
      aboutToAppear(): void {
        AppStorage.setOrCreate<NavPathStack>('basicNavigationStack', this.navStack);
        // ...
      }
    
      build() {
        Navigation(this.navStack) {
          // ...
        }
      }
    }
    
    @Builder
    export function BasicNavDestinationBuilder() {
      BasicNavDestination();
    }
    
    @Component
    struct BasicNavDestination {
      // 在NavDestination中获取导航控制器
      stack: NavPathStack = AppStorage.get<NavPathStack>('basicNavigationStack')!;
    
      build() {
        NavDestination() {
          // ...
        }
        .title('BasicNavDestination')
      }
    }
    
  • 方式二:NavDestination.onReady生命周期回调中获取。

    @Builder
    export function PageOneBuilder(name: string, param: string) {
      PageOne({ name: name, value: param });
    }
    
    @Component
    export struct PageOne {
      navPathStack: NavPathStack = new NavPathStack();
      name: string = '';
      @State value: string = '';
      context = this.getUIContext().getHostContext();
    
      build() {
        NavDestination() {
          // ...
        }.title(`${this.name}`)
        .onReady((ctx: NavDestinationContext) => {
          // 通过NavDestinationContext获取当前所在页面的导航控制器
          this.navPathStack = ctx.pathStack;
        })
      }
    }
    

基础操作

从API version 12开始,导航控制器允许被继承。开发者可以在派生类中自定义属性和方法,也可以重写父类的方法。派生类对象可以替代基类NavPathStack对象使用。重写NavPathStack的示例代码请参考定义导航控制器派生类。下文介绍了NavPathStack里提供的基础路由操作接口。

页面跳转

NavPathStack可以通过Push相关的接口(如pushPathpushPathByNamepushDestinationpushDestinationByName)实现页面跳转。跳转方式主要分为以下三种:

  1. 普通跳转:通过页面名称跳转,并可以携带参数。

    this.pageStack.pushPath({ name: 'pageOne', param: 'PageOne Param' });
    
    this.pageStack.pushPathByName('pageTwo', 'PageTwo Param');
    
  2. 带返回回调的跳转:跳转时添加onPop回调,能在页面出栈时获取返回信息,并进行处理。

    let DOMAIN = 0x0000;
    this.pageInfo.pushPathByName('temp4-pageTwo', 'temp4-pageTwo Param', (popInfo) => {
      hilog.info(DOMAIN, 'testTag', 'Pop page name is: ', popInfo.info.name, 'result: ',
        JSON.stringify(popInfo.result));
      // ...
    });
    
  3. 带错误码的跳转:跳转结束会触发异步回调,返回错误码信息。

    const DOMAIN = 0x0000;
    this.pageStack.pushDestination({
      name: 'pageTwo', param: 'PageTwo Param'}).catch((error: BusinessError) => {
      hilog.info(DOMAIN, 'testTag', '[pushDestination]failed', 'error code = ', error.code,
        'error.message = ', error.message);
    }).then(() => {
      hilog.info(DOMAIN, 'testTag', '[pushDestination]success.');
    });
    
    const DOMAIN = 0x0000;
    this.pageStack.pushDestinationByName('pageTwo', 'PageTwo Param').catch((error: BusinessError) => {
      hilog.info(DOMAIN, 'testTag', '[pushDestinationByName]failed', 'error code = ', error.code,
        'error.message = ', error.message);
    }).then(() => {
      hilog.info(DOMAIN, 'testTag', '[pushDestinationByName]success.');
    });
    

页面返回

NavPathStack可以通过pop相关接口实现页面返回,参考示例如下。

// 返回到上一页
this.pathStack.pop();
// 返回到上一个pageOne页面
this.pathStack.popToName('temp4-pageOne');
// 返回到索引为0的页面
this.pathStack.popToIndex(0);
// 返回到根首页(清除栈中所有页面)
this.pageStack.clear();

页面替换

NavPathStack可以通过Replace相关接口(如replacePathreplacePathByNamereplaceDestination)实现页面替换,参考示例如下。

// 将栈顶页面替换为pageTwo
this.pageStack.replacePath({ name: 'pageTwo', param: 'PageTwo Param' });
this.pageStack.replacePathByName('pageTwo', 'PageTwo Param');
const DOMAIN = 0x0000;
// 带错误码的替换,跳转结束会触发异步回调,返回错误码信息
this.pageStack.replaceDestination({ name: 'pageTwo', param: 'PageTwo Param' })
  .catch((error: BusinessError) => {
    hilog.info(DOMAIN, 'testTag', '[replaceDestination]failed', 'error code = ', error.code,
      'error.message = ', error.message);
  }).then(() => {
  hilog.info(DOMAIN, 'testTag', '[replaceDestination]success.');
})

页面删除

NavPathStack可以通过Remove相关接口(如removeByNameremoveByIndexesremoveByNavDestinationId)实现删除路由栈中特定页面的功能,参考示例如下。

// 删除栈中name为pageTwo的所有页面
this.pageStack.removeByName('pageTwo');
// 删除指定索引的页面
this.pageStack.removeByIndexes([1]);
// 删除指定id的页面
this.pageStack.removeByNavDestinationId('1');

移动页面

NavPathStack可以通过Move相关接口(如moveToTopmoveIndexToTop)实现移动路由栈中特定页面到栈顶的功能,参考示例如下。

// 移动栈中name为pageTwo的页面到栈顶
this.pageStack.moveToTop('pageTwo');
// 移动栈中索引为1的页面到栈顶
this.pageStack.moveIndexToTop(1);

单例跳转

通过设置LaunchMode为LaunchMode.MOVE_TO_TOP_SINGLETON或LaunchMode.POP_TO_SINGLETON,可以实现Navigation路由栈的单实例跳转。单实例跳转的规则如下:

  1. 如果指定为LaunchMode.MOVE_TO_TOP_SINGLETON,系统会从栈底到栈顶查找具有指定名称的NavDestination。找到后,该页面将被移动到栈顶(replace操作会用指定的NavDestination替换当前栈顶)。
  2. 如果指定为LaunchMode.POP_TO_SINGLETON,系统同样会从栈底到栈顶查找具有指定名称的NavDestination。找到后,会移除该NavDestination上方的所有页面(replace操作会用指定的NavDestination替换当前栈顶)。

当栈中存在的NavDestination页面通过单实例方式移动到栈顶时,将触发onNewParam回调。

有关单实例跳转的示例代码,可以参考使用导航控制器方法

参数获取

NavDestination子页第一次创建时会触发onReady回调,可以获取此页面对应的参数。

@Component
struct Page01 {
  pathStack: NavPathStack|undefined = undefined;
  // ...
  pageParam: string = '';
  build() {
    NavDestination() {
      // ...
    .title('Page01')
    .onReady((context: NavDestinationContext) => {
      this.pathStack = context.pathStack;
      this.pageParam = context.pathInfo.param as string;
    })
  }
}

NavDestination组件中可以通过设置onResult接口,接收返回时传递的路由参数。

class NavParam {
  desc: string = 'navigation-param'
};
const DOMAIN = 0x0000;
// ...
@Component
export struct PageOne {
  // ...
  build() {
    NavDestination() {
      // ...
    }
    // ...
    .onResult((param: Object) => {
      if (param instanceof NavParam) {
        console.info('TestTag', 'get NavParam, its desc: ' + (param as NavParam).desc);
        return;
      }
      console.info('TestTag', 'param not instance of NavParam');
    })
  }
}

其他业务场景,可以通过主动调用NavPathStack的获取接口(如getAllPathNamegetParamByIndexgetParamByNamegetIndexByName)获取指定页面的参数。

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

路由拦截

NavPathStack提供了setInterception方法,用于设置Navigation页面跳转拦截回调。该方法需要传入一个NavigationInterception对象,该对象包含多个回调函数,如willShow、didShow等,不同回调函数的调用时机不同,可根据业务需要选择拦截时机。

说明:

  • 无论是哪个回调,在进入回调时路由栈都已经发生了变化。
  • interception回调时机比willShow更早,也可以做拦截重定向的能力,区别是,前者触发时不会创建被拦截的页面,willShow触发时会创建被拦截的页面然后销毁。

以willShow为例,在回调中通过修改路由栈实现路由拦截重定向。

const DOMAIN = 0x0000;
this.pageStack.setInterception({
  willShow: (from: NavDestinationContext|'navBar', to: NavDestinationContext|'navBar',
    operation: NavigationOperation, animated: boolean) => {
    if (typeof to === 'string') {
      hilog.info(DOMAIN, 'testTag', 'target page is navigation home');
      return;
    }
    // 将跳转到PageTwo的路由重定向到PageOne
    let target: NavDestinationContext = to as NavDestinationContext;
    if (target.pathInfo.name === 'pageTwo') {
      target.pathStack.pop();
      target.pathStack.pushPathByName('pageOne', null);
    }
  }
})

示例

创建导航首页

实现步骤为:

  1. 使用Navigation创建导航主页,并创建导航控制器NavPathStack以此来实现不同页面之间的跳转。

  2. 在Navigation中增加List组件,来定义导航主页中不同的一级界面。

  3. 在List内的组件添加onClick方法,并在其中使用导航控制器NavPathStack的pushPathByName方法,使组件可以在点击之后从当前页面跳转到输入参数name在路由表内对应的页面。

@Entry
@Component
struct NavigationDemo {
  @Provide('navPathStack') navPathStack: NavPathStack = new NavPathStack();
  private listArray: Array<string> = ['WLAN', 'Bluetooth', 'Personal Hotspot', 'Connect & Share'];
  context = this.getUIContext().getHostContext();
  build() {
    Column() {
      Navigation(this.navPathStack) {
        // 请将$r('app.string.enterKeyWordsToSearch')替换为实际资源文件,在本示例中该资源文件的value值为"输入关键字搜索"
        TextInput({ placeholder: $r('app.string.enterKeyWordsToSearch') })
          .width('90%')
          .height(40)
          .margin({ bottom: 10 })

        // 通过List定义导航的一级界面
        List({ space: 12, initialIndex: 0 }) {
          ForEach(this.listArray, (item: string) => {
            ListItem() {
              Row() {
                Row() {
                  Text(`${item.slice(0, 1)}`)
                    .fontColor(Color.White)
                    .fontSize(14)
                    .fontWeight(FontWeight.Bold)
                }
                .width(30)
                .height(30)
                .backgroundColor('#a8a8a8')
                .margin({ right: 20 })
                .borderRadius(20)
                .justifyContent(FlexAlign.Center)

                Column() {
                  Text(item)
                    .fontSize(16)
                    .margin({ bottom: 5 })
                }
                .alignItems(HorizontalAlign.Start)

                Blank()

                Row()
                  .width(12)
                  .height(12)
                  .margin({ right: 15 })
                  .border({
                    width: { top: 2, right: 2 },
                    color: 0xcccccc
                  })
                  .rotate({ angle: 45 })
              }
              .borderRadius(15)
              .shadow({ radius: 100, color: '#ededed' })
              .width('90%')
              .alignItems(VerticalAlign.Center)
              .padding({ left: 15, top: 15, bottom: 15 })
              .backgroundColor(Color.White)
            }
            .width('100%')
            .onClick(() => {
              // $r('app.string.detailsPageParameters')需要替换为开发者所需的字符串资源文件,资源文件中的value值为“详情页面参数”
              this.navPathStack.pushPathByName(`${item}`,
                // 将name指定的NavDestination页面信息入栈,传递的参数为param
                this.context!.resourceManager.getStringSync($r('app.string.detailsPageParameters').id));
            })
          }, (item: string): string => item)
        }
        .listDirection(Axis.Vertical)
        .edgeEffect(EdgeEffect.Spring)
        .sticky(StickyStyle.Header)
        .chainAnimation(false)
        .width('100%')
      }
      .width('100%')
      .mode(NavigationMode.Auto)
      // $r('app.string.settings')需要替换为开发者所需的字符串资源文件,资源文件中的value值为“设置”
      .title($r('app.string.settings')) // 设置标题文字
    }
    .size({ width: '100%', height: '100%' })
    .backgroundColor(0xf4f4f5)
  }
}

创建导航子页

导航子页1实现步骤为:

  1. 使用NavDestination来创建导航子页PageOne。

  2. 创建导航控制器NavPathStack并在onReady时进行初始化,获取当前所在的导航控制器,以此来实现不同页面之间的跳转。

  3. 在子页面内的组件添加onClick,并在其中使用导航控制器NavPathStack的pop方法,使组件可以在点击之后弹出路由栈栈顶元素实现页面的返回。

@Builder
export function PageOneBuilder(name: string, param: string) {
  PageOne({ name: name, value: param });
}

@Component
export struct PageOne {
  navPathStack: NavPathStack = new NavPathStack();
  name: string = '';
  @State value: string = '';
  context = this.getUIContext().getHostContext();

  build() {
    NavDestination() {
      Column() {
        // $r('app.string.settingPage')需要替换为开发者所需的字符串资源文件,资源文件中的value值为“设置页面”
        Text(`${this.name}${this.context!.resourceManager.getStringSync($r('app.string.settingPage').id)}`)
          .width('100%')
          .fontSize(20)
          .fontColor(0x333333)
          .textAlign(TextAlign.Center)
          .textShadow({
            radius: 2,
            offsetX: 4,
            offsetY: 4,
            color: 0x909399
          })
          .padding({ top: 30 })
        Text(`${JSON.stringify(this.value)}`)
          .width('100%')
          .fontSize(18)
          .fontColor(0x666666)
          .textAlign(TextAlign.Center)
          .padding({ top: 45 })
        // $r('app.string.return')需要替换为开发者所需的字符串资源文件,资源文件中的value值为“返回”
        Button($r('app.string.return'))
          .width('50%')
          .height(40)
          .margin({ top: 50 })
          .onClick(() => {
            // 弹出路由栈栈顶元素,返回上个页面
            this.navPathStack.pop();
          })
      }
      .size({ width: '100%', height: '100%' })
    }.title(`${this.name}`)
    .onReady((ctx: NavDestinationContext) => {
      // 通过NavDestinationContext获取当前所在页面的导航控制器
      this.navPathStack = ctx.pathStack;
    })
  }
}

导航子页2实现步骤为:

  1. 使用NavDestination,来创建导航子页PageTwo。

  2. 创建导航控制器NavPathStack并在onReady时进行初始化,获取当前所在的导航控制器,以此来实现不同页面之间的跳转。

  3. 在子页面内的组件添加onClick,并在其中使用导航控制器NavPathStack的pushPathByName方法,使组件可以在点击之后从当前页面跳转到输入参数name在路由表内对应的页面。

@Builder
export function PageTwoBuilder(name: string) {
  PageTwo({ name: name });
}

@Component
export struct PageTwo {
  navPathStack: NavPathStack = new NavPathStack();
  name: string = '';
  private listArray: Array<string> = ['Projection', 'Print', 'VPN', 'Private DNS', 'NFC'];
  context = this.getUIContext().getHostContext();
  build() {
    NavDestination() {
      Column() {
        List({ space: 12, initialIndex: 0 }) {
          ForEach(this.listArray, (item: string) => {
            ListItem() {
              Row() {
                Row() {
                  Text(`${item.slice(0, 1)}`)
                    .fontColor(Color.White)
                    .fontSize(14)
                    .fontWeight(FontWeight.Bold)
                }
                .width(30)
                .height(30)
                .backgroundColor('#a8a8a8')
                .margin({ right: 20 })
                .borderRadius(20)
                .justifyContent(FlexAlign.Center)

                Column() {
                  Text(item)
                    .fontSize(16)
                    .margin({ bottom: 5 })
                }
                .alignItems(HorizontalAlign.Start)

                Blank()

                Row()
                  .width(12)
                  .height(12)
                  .margin({ right: 15 })
                  .border({
                    width: { top: 2, right: 2 },
                    color: 0xcccccc
                  })
                  .rotate({ angle: 45 })
              }
              .borderRadius(15)
              .shadow({ radius: 100, color: '#ededed' })
              .width('90%')
              .alignItems(VerticalAlign.Center)
              .padding({ left: 15, top: 15, bottom: 15 })
              .backgroundColor(Color.White)
            }
            .width('100%')
            .onClick(() => {
              // $r('app.string.pageSettingParam')需要替换为开发者所需的字符串资源文件,资源文件中的value值为“页面设置参数”
              this.navPathStack.pushPathByName(`${item}`,
                this.context!.resourceManager.getStringSync($r('app.string.pageSettingParam').id));
            })
          }, (item: string): string => item)
        }
        .listDirection(Axis.Vertical)
        .edgeEffect(EdgeEffect.Spring)
        .sticky(StickyStyle.Header)
        .width('100%')
      }
      .size({ width: '100%', height: '100%' })
    }.title(`${this.name}`)
    .onReady((ctx: NavDestinationContext) => {
      // NavDestinationContext获取当前所在的导航控制器
      this.navPathStack = ctx.pathStack;
    })
  }
}

创建路由表

实现步骤为:

  1. router_map.json中配置全局路由表,导航控制器NavPathStack可根据路由表中的name将对应页面信息入栈。

    {
      "routerMap" : [
        {
          "name" : "WLAN",
          "pageSourceFile"  : "src/main/ets/pages/PageOne.ets",
          "buildFunction" : "PageOneBuilder"
        },
        {
          "name" : "Bluetooth",
          "pageSourceFile"  : "src/main/ets/pages/PageOne.ets",
          "buildFunction" : "PageOneBuilder"
        },
        {
          "name" : "Personal Hotspot",
          "pageSourceFile"  : "src/main/ets/pages/PageOne.ets",
          "buildFunction" : "PageOneBuilder"
        },
        {
          "name" : "Connect & Share",
          "pageSourceFile"  : "src/main/ets/pages/PageTwo.ets",
          "buildFunction" : "PageTwoBuilder"
        },
        {
          "name" : "Projection",
          "pageSourceFile"  : "src/main/ets/pages/PageOne.ets",
          "buildFunction" : "PageOneBuilder"
        },
        {
          "name" : "Print",
          "pageSourceFile"  : "src/main/ets/pages/PageOne.ets",
          "buildFunction" : "PageOneBuilder"
        },
        {
          "name" : "VPN",
          "pageSourceFile"  : "src/main/ets/pages/PageOne.ets",
          "buildFunction" : "PageOneBuilder"
        },
        {
          "name" : "Private DNS",
          "pageSourceFile"  : "src/main/ets/pages/PageOne.ets",
          "buildFunction" : "PageOneBuilder"
        },
        {
          "name" : "NFC",
          "pageSourceFile"  : "src/main/ets/pages/PageOne.ets",
          "buildFunction" : "PageOneBuilder"
        }
      ]
    }
    
  2. 工程配置文件module.json5中配置{"routerMap": "$profile:router_map"}

zh-cn_image_0000001588458252

你可能感兴趣的鸿蒙文章

openharmony 鸿蒙 arkts-common-components-text-input

openharmony 鸿蒙 arkts-select-component-faq

openharmony 鸿蒙 arkui-overview

openharmony 鸿蒙 js-framework-syntax-css

openharmony 鸿蒙 arkts-popup-and-menu-components-popup

openharmony 鸿蒙 arkts-navigation-animation-faq

openharmony 鸿蒙 arkts-rotation-transition-animation

openharmony 鸿蒙 arkts-popup-and-menu-components-uicontext-popup

openharmony 鸿蒙 arkts-styled-string

openharmony 鸿蒙 arkts-attribute-animation-overview

  • 所属分类: 后端技术
  • 本文标签: 软件 鸿蒙
  • 版权声明: 本文链接 https://seaxiang.com/blog/5aanZNUk