本文同步发表于我的微信公众号,微信搜索 程语新视界 即可关注,每个工作日都有文章更新

一. 概述

Navigation 是 HarmonyOS 中用于实现页面路由导航的根视图容器,一般作为页面的根容器使用。它支持在不同 Navigation 页面(NavDestination)间进行跳转,传递参数,并提供灵活的跳转栈操作。

核心特性

  • 三种显示模式:单栏(Stack)、分栏(Split)、自适应(Auto)

  • 组件级路由能力:实现自然流畅的转场体验

  • 多端适配:自动适配窗口大小,大窗口下自动切换分栏模式

  • 丰富的标题栏样式:提供更好的标题和内容联动效果

二. 显示模式

2.1 自适应模式(默认)

Navigation() {
  // 页面内容
}
.mode(NavigationMode.Auto)
  • 宽度阈值:API 9及以前 520vp,API 10及以后 600vp

  • 自动切换:超过阈值时分栏模式,否则单栏模式

2.2 单栏模式

Navigation() {
  // 页面内容
}
.mode(NavigationMode.Stack)
  • 适用于窄屏设备

  • 路由跳转时整个页面被替换

2.3 分栏模式

@Entry
@Component
struct SplitNavigationExample {
  @State menuItems: ToolbarItem = {
    'value': "功能",
    'icon': "./image/function_icon.svg",
    'action': () => {}
  }

  @Provide('navigationStack') navStack: NavPathStack = new NavPathStack()
  private pageNumbers: number[] = [1, 2, 3];

  @Builder
  buildPageContent(pageName: string) {
    if (pageName === "PageOne") {
      FirstPage();
    } else if (pageName === "PageTwo") {
      SecondPage();
    } else if (pageName === "PageThree") {
      ThirdPage();
    }
  }

  build() {
    Column() {
      Navigation(this.navStack) {
        TextInput({ placeholder: '输入搜索内容...' })
          .width("90%")
          .height(40)
          .backgroundColor('#FFFFFF')
          
        List({ space: 12 }) {
          ForEach(this.pageNumbers, (pageNum: number) => {
            ListItem() {
              Text("页面" + pageNum)
                .width("100%")
                .height(72)
                .backgroundColor('#FFFFFF')
                .borderRadius(24)
                .fontSize(16)
                .fontWeight(500)
                .textAlign(TextAlign.Center)
                .onClick(() => {
                  this.navStack.pushPath({ name: "Page" + pageNum });
                })
            }
          }, (pageNum: number) => pageNum.toString())
        }
        .width("90%")
        .margin({ top: 12 })
      }
      .title("应用主标题")
      .mode(NavigationMode.Split)
      .navDestination(this.buildPageContent)
      .menus([
        {
          value: "", icon: "./image/search_icon.svg", action: () => {}
        },
        {
          value: "", icon: "./image/add_icon.svg", action: () => {}
        }
      ])
      .toolbarConfiguration([this.menuItems, this.menuItems])
    }
    .height('100%')
    .width('100%')
    .backgroundColor('#F1F3F5')
  }
}

三. 标题栏设置

3.1 Mini 模式

  • 普通型标题栏,适用于一级页面不需要突出标题的场景

Navigation() {
  // 内容
}
.titleMode(NavigationTitleMode.Mini)

3.2 Full 模式

  • 强调型标题栏,适用于一级页面需要突出标题的场景

Navigation() {
  // 内容
}
.titleMode(NavigationTitleMode.Full)

四. 菜单栏和工具栏

4.1 菜单栏设置

let menuItem: NavigationMenuItem = {
  'value': "", 
  'icon': "./image/menu_icon.svg", 
  'action': () => {}
}

Navigation() {
  // 内容
}
.menus([menuItem, menuItem, menuItem])
  • 竖屏:最多显示3个图标,多余放入更多图标

  • 横屏:最多显示5个图标

4.2 工具栏设置

let toolbarItem: ToolbarItem = {
  'value': "操作", 
  'icon': "./image/tool_icon.svg", 
  'action': () => {}
};
let toolbarItems: ToolbarItem[] = [toolbarItem, toolbarItem];

Navigation() {
  // 内容
}
.toolbarConfiguration(toolbarItems)

五. 路由操作

5.1 导航控制器初始化

@Entry
@Component
struct MainApp {
  appNavigationStack: NavPathStack = new NavPathStack();

  build() {
    Navigation(this.appNavigationStack) {
      // 应用内容
    }
    .title('应用主页')
  }
}

5.2 页面跳转

// 普通跳转
this.navStack.pushPath({ name: "DetailPage", param: "页面参数" });
this.navStack.pushPathByName("DetailPage", "页面参数");

// 带返回回调的跳转
this.navStack.pushPathByName('DetailPage', "参数内容", (popInfo) => {
  console.info('页面返回: ' + popInfo.info.name + ', 结果: ' + JSON.stringify(popInfo.result));
});

// 带错误处理的跳转
this.navStack.pushDestination({name: "DetailPage", param: "参数内容"})
  .catch((error: BusinessError) => {
    console.error(`跳转失败, 错误码: ${error.code}, 错误信息: ${error.message}`);
  }).then(() => {
    console.info('跳转成功');
  });

5.3 页面返回

// 返回上一页
this.navStack.pop();

// 返回到指定页面
this.navStack.popToName("HomePage");

// 返回到指定索引页面
this.navStack.popToIndex(1);

// 清空栈返回到首页
this.navStack.clear();

5.4 页面替换和删除

// 页面替换
this.navStack.replacePath({ name: "NewPage", param: "新参数" });
this.navStack.replacePathByName("NewPage", "新参数");

// 页面删除
this.navStack.removeByName("OldPage");
this.navStack.removeByIndexes([1, 3, 5]);
this.navStack.removeByNavDestinationId("page123");

5.5 页面移动

// 移动页面到栈顶
this.navStack.moveToTop("ImportantPage");
this.navStack.moveIndexToTop(2);

六. 参数传递和获取

6.1 页面参数获取

@Component
struct DetailPage {
  navigationStack: NavPathStack | undefined = undefined;
  receivedParam: string = '';

  build() {
    NavDestination() {
      Column() {
        Text('接收到的参数: ' + this.receivedParam)
      }
    }
    .title('详情页')
    .onReady((context: NavDestinationContext) => {
      this.navigationStack = context.pathStack;
      this.receivedParam = context.pathInfo.param as string;
    })
  }
}

6.2 接收返回时传递的路由参数

class ReturnData {
  message: string = '返回数据内容'
}

@Component
struct DataEntryPage {
  build() {
    NavDestination() {
      Column() {
        Button('返回并传递数据')
          .onClick(() => {
            // 返回操作
          })
      }
    }
    .onResult((data: Object) => {
      if (data instanceof ReturnData) {
        console.info('接收到返回数据: ' + (data as ReturnData).message);
        return;
      }
      console.info('返回数据类型不匹配');
    })
  }
}

七. 路由拦截

this.navStack.setInterception({
  willShow: (from: NavDestinationContext | "navBar", to: NavDestinationContext | "navBar",
            operation: NavigationOperation, animated: boolean) => {
    if (typeof to === "string") {
      console.info("目标页面是导航首页");
      return;
    }

    // 路由重定向示例
    let target: NavDestinationContext = to as NavDestinationContext;
    if (target.pathInfo.name === 'RestrictedPage') {
      target.pathStack.pop();
      target.pathStack.pushPathByName('LoginPage', null);
    }
  }
})

八. 子页面管理

8.1 标准页面类型

@Component
struct StandardPage {
  @Consume('navigationStack') navStack: NavPathStack;

  build() {
    NavDestination() {
      Column() {
        Text('标准页面内容')
      }
    }
    .title('标准页面')
    .mode(NavDestinationMode.STANDARD)
  }
}

8.2 弹窗页面类型

@Component
struct ModalDialogPage {
  @Consume('navigationStack') navStack: NavPathStack;

  build() {
    NavDestination() {
      Stack({ alignContent: Alignment.Center }) {
        Column() {
          Text("弹窗内容")
            .fontSize(20)
            .margin({ bottom: 100 })
          Button("关闭弹窗")
            .onClick(() => {
              this.navStack.pop();
            })
            .width('40%')
        }
        .justifyContent(FlexAlign.Center)
        .backgroundColor(Color.White)
        .borderRadius(15)
        .height('35%')
        .width('85%')
      }
      .height("100%")
      .width('100%')
    }
    .backgroundColor('rgba(0,0,0,0.6)')
    .hideTitleBar(true)
    .mode(NavDestinationMode.DIALOG)
  }
}

九. 页面生命周期

  1. aboutToAppear - 自定义组件创建后,build() 执行前

  2. onWillAppear - NavDestination 创建后,挂载到组件树前

  3. onAppear - 组件挂载到组件树时

  4. onWillShow - 页面布局显示前

  5. onShown - 页面布局显示完成

  6. onActive - 页面处于激活态

  7. onWillHide - 页面触发隐藏前

  8. onInactive - 页面处于非激活态

  9. onHidden - 页面触发隐藏后

  10. onWillDisappear - 页面即将销毁前

  11. onDisappear - 组件从组件树卸载时

  12. aboutToDisappear - 自定义组件析构前

十. 页面转场动画

10.1 关闭系统转场

// 全局关闭
this.navStack.disableAnimation(true);

// 单次关闭
this.navStack.pushPath({ name: "NextPage" }, false);
this.navStack.pop(false);

10.2 自定义转场

// Navigation 级别自定义转场
Navigation(this.navStack) {
  // 内容
}
.customNavContentTransition(() => {
  // 返回自定义转场协议对象
})

// NavDestination 级别自定义转场
NavDestination() {
  // 页面内容
}
.customTransition(() => {
  // 返回页面转场代理
})

10.3 共享元素转场

// 起始页面
NavDestination() {
  Column() {
    Image($r('app.media.sharedImage'))
      .geometryTransition('transitionId')
      .width(100)
      .height(100)
  }
}
.title('起始页')

// 目标页面  
NavDestination() {
  Column() {
    Image($r('app.media.sharedImage'))
      .geometryTransition('transitionId')
      .width(200)
      .height(200)
  }
}
.title('目标页')

// 跳转时启用动画
Button('跳转')
  .onClick(() => {
    this.getUIContext()?.animateTo({ duration: 800 }, () => {
      this.navStack.pushPath({ name: 'TargetPage' }, false)
    });
  })

十一. 跨包跳转

11.1 系统路由表(推荐)

module.json5 配置:

{
  "module": {
    "routerMap": "$profile:route_config"
  }
}

route_config.json 配置:

{
  "routerMap": [
    {
      "name": "ProductDetail",//跳转页面名称
      "pageSourceFile": "src/main/ets/pages/ProductDetail.ets",
       //目标页在包内的路径,相对src目录的相对路径
      "buildFunction": "ProductDetailBuilder",
       //目标页的入口函数名称,必须以@Builder修饰
      "data": {
        "category": "electronics"
      }
      //应用自定义字段。可以通过配置项读取接口getConfigInRouteMap获取
    }
  ]
}

页面构建函数:

@Builder
export function ProductDetailBuilder() {
  ProductDetailPage();
}

@Component
struct ProductDetailPage {
  navStack: NavPathStack = new NavPathStack();

  build() {
    NavDestination() {
      // 页面内容
    }
    .title('商品详情')
    .onReady((context: NavDestinationContext) => {
      this.navStack = context.pathStack;
    })
  }
}

11.2 自定义路由表

// 动态 import 方式(推荐)
@Builder
buildPageRouter(pageName: string) {
  if (pageName === "DynamicPage") {
    DynamicImportHelper.loadDynamicPage();
  }
}

Navigation(this.navStack) {
  // 内容
}
.navDestination(this.buildPageRouter)

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐