开天辟地 HarmonyOS(鸿蒙) - 组件(导航类): Navigation(导航组件的导航)

源码 https://github.com/webabcd/HarmonyDemo
作者 webabcd

开天辟地 HarmonyOS(鸿蒙) - 组件(导航类): Navigation(导航组件的导航)

示例如下:

pages\component\navigation\NavigationDemo2.ets

/*
 * Navigation - 导航组件
 * 包括顶部的导航栏,中部的内容区,底部的工具栏
 *
 * 本例用于演示导航组件的导航
 * Navigation 是导航的根组件,导航到的目标页是 NavDestination 组件,导航栈中包含的是多个 NavDestination 组件(Navigation 组件不在导航栈中)
 * 如果需要通过 Navigation 实现导航,则先要配置路由表
 * 1、在 src/main/module.json5 中做类似如下的配置(指定路由表)
 * {
 *   "module": {
 *     "routerMap": "$profile:route_map"
 *   }
 * }
 * 2、在 src/main/resources/profile/route_map.json 中配置路由表
 * {
 *   "routerMap": [
 *     {
 *       // 路由的名称
 *       "name": "navigationDemo2_page1",
 *       // 指定一个页面地址,此页面中包含 buildFunction 指定的函数
 *       "pageSourceFile": "src/main/ets/pages/component/navigation/pages/NavigationDemo2_Page1.ets",
 *       // 指定一个 @Builder 函数的名称,其用于构造此路由指向的组件
 *       "buildFunction": "navigationDemo2_page1_builder"
 *     }
 *   ]
 * }
 *
 * 注:本例演示的是如何通过路由表(允许非 @Entry 组件)做路由,如需要通过页面地址做路由(必须是 @Entry 组件)请参见 RouterDemo.ets 中的说明
 */

import { TitleBar, RadioBar } from '../../TitleBar';
import { LengthMetrics, SymbolGlyphModifier } from '@kit.ArkUI';

@Entry
@Component
struct NavigationDemo {

  build() {
    Column() {
      TitleBar()
      Tabs() {
        TabContent() { MySample1() }.tabBar('导航的基础').align(Alignment.Top)
        TabContent() { MySample2() }.tabBar('导航的拦截').align(Alignment.Top)
        TabContent() { MySample3() }.tabBar('入栈的模式').align(Alignment.Top)
      }
      .scrollable(true)
      .barMode(BarMode.Scrollable)
      .layoutWeight(1)
    }
  }
}

@Component
struct MySample1 {

  /*
   * NavPathStack - 导航栈(实例化 Navigation 时指定一个 NavPathStack 对象,后续可以通过此 NavPathStack 对象实现导航)
   *   pushPath() - 将指定的页面入栈
   *     info - 入栈页面的信息(一个 NavPathInfo 对象)
   *       name - 目标路由的名称(即 routerMap 中的 name)
   *       param - 传给目标页的数据
   *       isEntry - 目标页是否是 @Entry 装饰的
   *       onPop - 目标页出栈且传回了数据时的回调(回调参数是一个 PopInfo 对象)
   *         info - 出栈的页的 NavPathInfo 对象
   *         result - 出栈的页传回的数据
   *     options - 选项(一个 NavigationOptions 对象)
   *       animated - 是否启用转场动画
   *       launchMode - 页面入栈的模式
   *   pushPathByName(name: string, param: Object, onPop: Callback<PopInfo>, animated?: boolean) - 将指定的页面入栈,参数的用法可以参考 pushPath() 的说明
   *   pushDestination(info: NavPathInfo, animated?: boolean) - 将指定的页面入栈,参数的用法可以参考 pushPath() 的说明
   *     返回一个 Promise 对象
   *   pushDestinationByName(name: string, param: Object, animated?: boolean) - 将指定的页面入栈,参数的用法可以参考 pushPath() 的说明
   *     返回一个 Promise 对象
   *   disableAnimation() - 是否禁用所有转场动画
   *   getParent() - 当 Navigation 嵌套 Navigation 时,可以获取当前 Navigation 的父 Navigation 的 NavPathStack 对象
   *
   * 注:本例导航到的页面请参见 component/navigation/pages/NavigationDemo2_Page1.ets
   */
  navPathStack: NavPathStack = new NavPathStack()

  @State message: string = ""

  aboutToAppear(): void {
    this.navPathStack.disableAnimation(false)
    let parent_navPathStack = this.navPathStack.getParent()
  }

  build() {
    Navigation(this.navPathStack) {
      Column({space:10}) {

        Button('pushPath').onClick(() => {
          this.navPathStack.pushPath({
            name: 'navigationDemo2_page1',
            param: "abc",
            isEntry: false,
            onPop: (popInfo: PopInfo) => {
              this.message += `onPop name:${popInfo.info.name}, result:${popInfo.result}\n`
            },
          }, {
            animated: true,
            launchMode: LaunchMode.STANDARD
          })
        })

        Button('pushPathByName').onClick(() => {
          this.navPathStack.pushPathByName(
            'navigationDemo2_page1',
            "abc",
            (popInfo: PopInfo) => {
              this.message += `onPop name:${popInfo.info.name}, result:${popInfo.result}\n`
            },
            true
          )
        })

        Button('pushDestination').onClick(() => {
          this.navPathStack.pushDestination({
            name: 'navigationDemo2_page1',
            param: "abc",
            onPop: (popInfo: PopInfo) => {
              this.message += `onPop name:${popInfo.info.name}, result:${popInfo.result}\n`
            }
          }, {
            animated: true,
            launchMode: LaunchMode.STANDARD
          }).catch((error: Error) => {
            this.message += "pushDestination err\n"
          }).then(() => {
            this.message += "pushDestination ok\n"
          });
        })

        Button('pushDestinationByName').onClick(() => {
          this.navPathStack.pushDestinationByName(
            'navigationDemo2_page1',
            "abc",
            (popInfo: PopInfo) => {
              this.message += `onPop ${popInfo.info.name} ${popInfo.result}\n`
            },
            true
          ).catch((error: Error) => {
            this.message += "pushDestinationByName err\n"
          }).then(() => {
            this.message += "pushDestinationByName ok\n"
          });
        })

        Scroll() {
          Text(this.message)
        }
        .layoutWeight(1)
      }
    }
    .titleMode(NavigationTitleMode.Mini)
    .title("NavigationDemo_MySample1")
  }
}

@Component
struct MySample2 {

  navPathStack: NavPathStack = new NavPathStack()

  build() {
    Navigation(this.navPathStack) {
      Column({space:10}) {

        /*
         * 本例用于演示导航的拦截,请参见 component/navigation/pages/NavigationDemo2_Page2.ets
         */
        Button('pushPath').onClick(() => {
          this.navPathStack.pushPath({
            name: 'navigationDemo2_page2',
          })
        })
      }
    }
    .titleMode(NavigationTitleMode.Mini)
    .title("NavigationDemo_MySample2")
  }
}

@Component
struct MySample3 {

  navPathStack: NavPathStack = new NavPathStack()

  @State message: string = ""

  build() {
    Navigation(this.navPathStack) {
      Column({space:10}) {

        /*
         * 本例用于演示入栈的模式,请参见 component/navigation/pages/NavigationDemo2_Page3.ets
         */
        Button('pushPath').onClick(() => {
          this.navPathStack.pushPath({
            name: 'navigationDemo2_page3',
          }, {
            launchMode: LaunchMode.STANDARD
          })
        })
      }
    }
    .titleMode(NavigationTitleMode.Mini)
    .title("NavigationDemo_MySample3")
  }
}

pages\component\navigation\pages\NavigationDemo2_Page1.ets

/*
 * NavDestination - 导航组件(Navigation)导航到的目标页
 */

import { LengthMetrics } from '@kit.ArkUI'

// 路由表中 routerMap 的 buildFunction 指定的 @Builder 函数,用于构造此路由指向的组件
// name - 路由名称
// param - 传递过来的数据
@Builder function navigationDemo2_page1_builder(name: string, param: Object) {
  NavigationDemo2_Page1({
    message: `路由名称:${name}, 参数:${param}\n\n`
  })
}

@Component
struct NavigationDemo2_Page1 {

  /*
   * NavPathStack - 导航栈
   *   replacePath() - 栈顶出栈,然后将指定的页面入栈
   *     info - 入栈页面的信息(一个 NavPathInfo 对象,关于 NavPathInfo 请参见 NavigationDemo2.ets 中的说明)
   *     options - 选项(一个 NavigationOptions 对象)
   *       animated - 是否支持转场动画
   *       launchMode - 页面入栈的模式
   *   replacePathByName(name: string, param: Object, animated?: boolean) - 栈顶出栈,然后将指定的页面入栈
   *   removeByName() - 让指定名称的所有页面出栈
   *   removeByIndexes() - 让指定的所有位置的页面出栈
   *   removeByNavDestinationId() - 让指定 navDestinationId 的页面出栈
   *   pop() - 出栈并传回数据(传回了数据则会触发入栈时的 onPop 回调,否则不触发)
   *   popToName() - 路由回退到从栈底开始的第一个指定名称的页面
   *   popToIndex() - 路由回退到指定索引位置的页面
   *   moveToTop() - 将从栈底开始的第一个指定名称的页面移动到栈顶
   *   moveIndexToTop() - 将指定索引位置的页面移动到栈顶
   *   clear() - 清理当前导航栈的全部页面
   *   size() - 导航栈中所有 NavDestination 的数量
   *   getAllPathName() - 导航栈中所有 NavDestination 的名称
   *   getParamByIndex() - 导航栈中指定索引位置的 NavDestination 的参数信息(入栈时传递过来的数据)
   *   getParamByName() - 导航栈中指定名称的所有 NavDestination 的参数信息(入栈时传递过来的数据)
   *   getIndexByName() - 导航栈中指定名称的所有 NavDestination 的索引位置
   *
   * 注:可以认为导航栈中的每个页面都是一个 NavDestination 组件(导航栈中不包含 Navigation 组件)
   */
  navPathStack: NavPathStack = new NavPathStack()

  navDestinationId: string = "0"

  @State message: string = ""

  aboutToAppear(): void {
    /*
     * queryNavigationInfo() - 组件的内置方法,可以获取当前的 NavigationInfo
     *   navigationId - 当前 Navigation 的标识(由系统自动生成)
     *   pathStack - 当前 Navigation 的 NavPathStack 对象
     */
    let navigationInfo = this.queryNavigationInfo();
    this.message += `navigationInfo: ${JSON.stringify(navigationInfo)}\n\n`
  }

  build() {

    /*
     * NavDestination - 导航组件(Navigation)导航到的目标页
     *   onReady() - 当 NavDestination 构造子组件之前时的回调(回调参数是一个 NavDestinationContext 对象)
     *     pathInfo - 页面的信息(一个 NavPathInfo 对象,关于 NavPathInfo 请参见 NavigationDemo2.ets 中的说明)
     *     pathStack - 当前的导航栈(一个 NavPathStack 对象)
     *     navDestinationId - 当前 NavDestination 的标识(由系统自动生成)
     */
    NavDestination() {
      Column({space:10}) {

        Flex({ wrap: FlexWrap.Wrap, justifyContent: FlexAlign.SpaceAround, space: { cross: LengthMetrics.vp(10) } }) {
          Button('pushPath').onClick(() => {
            this.navPathStack.pushPath({
              name: 'navigationDemo2_page1',
              param: "abc",
              onPop: (popInfo: PopInfo) => {
                this.message += `onPop name:${popInfo.info.name}, result:${popInfo.result}\n\n`
              }
            })
          })

          Button('replacePath').onClick(() => {
            this.navPathStack.replacePath({
              name: 'navigationDemo2_page1',
              param: "abc",
              onPop: (popInfo: PopInfo) => {
                this.message += `onPop name:${popInfo.info.name}, result:${popInfo.result}\n\n`
              }
            }, {
              animated: true,
              launchMode: LaunchMode.STANDARD
            })
          })

          Button('replacePathByName').onClick(() => {
            this.navPathStack.replacePathByName('navigationDemo2_page1', "abc", true)
          })

          Button('removeByName').onClick(() => {
            this.navPathStack.removeByName('navigationDemo2_page1')
          })

          Button('removeByIndexes').onClick(() => {
            this.navPathStack.removeByIndexes([this.navPathStack.size() - 1])
          })

          Button('removeByNavDestinationId').onClick(() => {
            this.navPathStack.removeByNavDestinationId(this.navDestinationId)
          })

          Button('pop').onClick(() => {
            // 出栈并传回数据,会触发入栈时的 onPop 回调
            //   第 1 个参数是传回的数据
            //   第 2 个参数用于说明是否启用转场动画
            //   返回值为出栈页面的信息(一个 NavPathInfo 对象,关于 NavPathInfo 请参见 NavigationDemo2.ets 中的说明)
            let info = this.navPathStack.pop("xyz", true);

            // 出栈但是不传回数据,不会触发入栈时的 onPop 回调
            // let info = this.navPathStack.pop(true);
          })

          Button('popToName').onClick(() => {
            // 路由回退到从栈底开始的第一个名为 navigationDemo2_page1 的页面
            //   第 1 个参数是指定的页面的名称
            //   第 2 个参数是传回的数据
            //   第 3 个参数用于说明是否启用转场动画
            //   返回值为从栈底开始的第一个名为 navigationDemo2_page1 的页面的索引位置,没有的话则返回 -1
            let index = this.navPathStack.popToName("navigationDemo2_page1", "xyz", true);
          })

          Button('popToIndex').onClick(() => {
            // 路由回退到索引位置为 0 的页面
            //   第 1 个参数是指定的页面的索引位置
            //   第 2 个参数是传回的数据
            //   第 3 个参数用于说明是否启用转场动画
            this.navPathStack.popToIndex(0, "xyz", true);
          })

          Button('moveToTop').onClick(() => {
            // 将从栈底开始的第一个名为 navigationDemo2_page1 的页面移动到栈顶
            //   第 1 个参数是指定的页面的名称
            //   第 2 个参数用于说明是否启用转场动画
            //   返回值为从栈底开始的第一个名为 navigationDemo2_page1 的页面的索引位置,没有的话则返回 -1
            let index = this.navPathStack.moveToTop("navigationDemo2_page1", true);
          })

          Button('moveIndexToTop').onClick(() => {
            // 将索引位置为 0 的页面移动到栈顶
            //   第 1 个参数是指定的页面的索引位置
            //   第 2 个参数用于说明是否启用转场动画
            this.navPathStack.moveIndexToTop(0, true);
          })

          Button('clear').onClick(() => {
            // 清理当前导航栈的全部页面
            //   第 1 个参数用于说明是否启用转场动画
            this.navPathStack.clear(true)
          })
        }

        Scroll() {
          Text(this.message)
        }
        .layoutWeight(1)
      }
    }
    .title('NavigationDemo2_Page1')
    .onReady((context: NavDestinationContext) => {
      this.navPathStack = context.pathStack
      this.navDestinationId = `${context.navDestinationId}`

      this.message += `navDestinationId:${context.navDestinationId}\n\n`
      this.message += `路由名称:${context.pathInfo.name}, 参数:${context.pathInfo.param}\n\n`
      this.message += `导航栈中所有 NavDestination 的数量:\n${this.navPathStack.size()}\n\n`
      this.message += `导航栈中所有 NavDestination 的名称:\n${JSON.stringify(this.navPathStack.getAllPathName())}\n\n`
      this.message += `导航栈中第 1 个 NavDestination 的参数信息:\n${JSON.stringify(this.navPathStack.getParamByIndex(0))}\n\n`
      this.message += `导航栈中指定名称的所有 NavDestination 的参数信息:\n${JSON.stringify(this.navPathStack.getParamByName("navigationDemo2_page1"))}\n\n`
      this.message += `导航栈中指定名称的所有 NavDestination 的索引位置:\n${JSON.stringify(this.navPathStack.getIndexByName("navigationDemo2_page1"))}\n\n`
    })
  }
}

pages\component\navigation\pages\NavigationDemo2_Page2.ets

/*
 * NavDestination - 导航组件(Navigation)导航到的目标页
 */

import { promptAction } from '@kit.ArkUI'

// 路由表中 routerMap 的 buildFunction 指定的 @Builder 函数,用于构造此路由指向的组件
// name - 路由名称
// param - 传递过来的数据
@Builder function navigationDemo2_page2_builder(name: string, param: Object) {
  NavigationDemo2_Page2()
}

@Component
struct NavigationDemo2_Page2 {

  /*
   * NavPathStack - 导航栈
   *   onReady() - 当 NavDestination 构造子组件之前时的回调(回调参数是一个 NavDestinationContext 对象)
   *     pathInfo - 页面的信息(一个 NavPathInfo 对象,关于 NavPathInfo 请参见 NavigationDemo2.ets 中的说明)
   *     pathStack - 当前的导航栈(一个 NavPathStack 对象)
   *     navDestinationId - 当前 NavDestination 的标识(由系统自动生成)
   *   setInterception() - 设置当前导航栈的拦截行为(指定一个 NavigationInterception 对象)
   *     willShow(), didShow() - 页面跳转前和跳转后的回调
   *       from - 页面跳转之前的栈顶页面信息(一个 NavDestinationContext 对象),如果此值为 "navBar" 则表示跳转前的页为 Navigation 组件
   *       to - 页面跳转之后的栈顶页面信息(一个 NavDestinationContext 对象),如果此值为 "navBar" 则表示跳转的目标页为 Navigation 组件
   *       operation - 页面跳转的类型(NavigationOperation 枚举)
   *         PUSH	- 此次转场为页面进场
   *         POP - 此次转场为页面退场
   *         REPLACE - 此次转场为页面替换
   *       isAnimated -	页面跳转是否启用了转场动画
   *     modeChange() - 导航组件的显示模式发生变化时的回调(关于导航组件的显示模式请参见 NavigationDemo.ets 中的说明)
   *   onBackPressed() - 拦截返回行为
   *     return false 则使用默认的返回行为
   *     return true 则使用自定义的返回行为
   */
  navPathStack: NavPathStack = new NavPathStack()

  @State message: string = ""

  build() {
    NavDestination() {
      Column({space:10}) {
        Button('pushPath').onClick(() => {
          this.navPathStack.pushPath({
            name: 'navigationDemo2_page2',
          })
        })

        Scroll() {
          Text(this.message)
        }
        .layoutWeight(1)
      }
    }
    .title('NavigationDemo2_Page2')
    .onBackPressed(() => {
      // 自定义返回行为
      promptAction.showDialog({
        title: '确认返回码?',
        buttons: [ { text: '确认', color: '#ff0000' }, ]
      }, (err, data) => {
        if (!err) {
          this.navPathStack.pop()
        }
      });
      return true
    })
    .onReady((context: NavDestinationContext) => {
      this.navPathStack = context.pathStack;

      this.navPathStack.setInterception({
        // 页面跳转前拦截
        willShow: (from: NavDestinationContext | "navBar", to: NavDestinationContext | "navBar", operation: NavigationOperation, isAnimated: boolean) => {
          this.message += `willShow from:${JSON.stringify(from)}\n\n`
          this.message += `willShow to:${JSON.stringify(to)}\n\n`
          this.message += `willShow operation:${JSON.stringify(operation)}\n\n`
          this.message += `willShow animated:${JSON.stringify(isAnimated)}\n\n`
        },
        // 页面跳转后回调
        didShow: (from: NavDestinationContext | "navBar", to: NavDestinationContext | "navBar",  operation: NavigationOperation, isAnimated: boolean) => {
          this.message += `didShow from:${JSON.stringify(from)}\n\n`
          this.message += `didShow to:${JSON.stringify(to)}\n\n`
          this.message += `didShow operation:${JSON.stringify(operation)}\n\n`
          this.message += `didShow animated:${JSON.stringify(isAnimated)}\n\n`
        },
        // 导航组件的显示模式发生变化时的回调
        modeChange: (mode: NavigationMode) => {
          this.message += `modeChange mode:${JSON.stringify(mode)}\n\n`
        }
      })
    })
  }
}

pages\component\navigation\pages\NavigationDemo2_Page3.ets

/*
 * NavDestination - 导航组件(Navigation)导航到的目标页
 */

// 路由表中 routerMap 的 buildFunction 指定的 @Builder 函数,用于构造此路由指向的组件
// name - 路由名称
// param - 传递过来的数据
@Builder function navigationDemo2_page3_builder(name: string, param: Object) {
  NavigationDemo2_Page3()
}

@Component
struct NavigationDemo2_Page3 {

  /*
   * NavPathStack - 导航栈
   *   pushPath() - 将指定的页面入栈
   *     info - 入栈页面的信息(一个 NavPathInfo 对象)
   *     options - 选项(一个 NavigationOptions 对象)
   *       animated - 是否启用转场动画
   *       launchMode - 页面入栈的模式(LaunchMode 枚举)
   *         STANDARD - 标准的入栈
   *         MOVE_TO_TOP_SINGLETON - 从栈底向栈顶查找,如果存在相同名称的页面,则把这个相同名称的页面移动到栈顶,否则行为和 STANDARD 一样
   *         POP_TO_SINGLETON - 从栈底向栈顶查找,如果存在相同名称的页面,则路由会回退到这个相同名称的页面,否则行为和 STANDARD 一样
   *         NEW_INSTANCE - 保证使用新的页面实例入栈
   */
  navPathStack: NavPathStack = new NavPathStack()

  @State message: string = ""

  build() {
    NavDestination() {
      Column({space:10}) {

        Text(this.message)

        Button('LaunchMode.STANDARD').onClick(() => {
          this.navPathStack.pushPath({
            name: 'navigationDemo2_page3',
          }, {
            launchMode: LaunchMode.STANDARD
          })
        })

        Button('LaunchMode.MOVE_TO_TOP_SINGLETON').onClick(() => {
          this.navPathStack.pushPath({
            name: 'navigationDemo2_page3',
          }, {
            launchMode: LaunchMode.MOVE_TO_TOP_SINGLETON
          })
        })

        Button('LaunchMode.POP_TO_SINGLETON').onClick(() => {
          this.navPathStack.pushPath({
            name: 'navigationDemo2_page3',
          }, {
            launchMode: LaunchMode.POP_TO_SINGLETON
          })
        })

        Button('LaunchMode.NEW_INSTANCE').onClick(() => {
          // 连续操作导航栈时,会忽略中间过程,只显示最终的结果
          // 比如:
          // 1、先 pop 再通过 STANDARD 方式 push 相同名称的页面的话,系统会认为操作前后的结果一致从而不做任何处理
          // 2、先 pop 再通过 NEW_INSTANCE 方式 push 相同名称的页面的话,系统会先 pop 页面,然后再 push 一个新的页面实例
          this.navPathStack.pop()
          this.navPathStack.pushPath({
            name: 'navigationDemo2_page3',
          }, {
            launchMode: LaunchMode.NEW_INSTANCE
          })
        })
      }
    }
    .title('NavigationDemo2_Page3')
    .onReady((context: NavDestinationContext) => {
      this.navPathStack = context.pathStack;
      this.message = `navDestinationId:${context.navDestinationId}`
    })
  }
}

源码 https://github.com/webabcd/HarmonyDemo
作者 webabcd

posted @ 2025-02-06 08:15  webabcd  阅读(175)  评论(0)    收藏  举报