# 实现Navigation首页和子页面互相跳转时的显隐监听

#### 问题现象

一个应用的UI页面采用Navigation组件作为根视图，并使用NavPathStack进行页面跳转。在onPageShow事件不支持或不执行的情况下，如何监听Navigation首页和子页面互相跳转的过程中两个页面的显示和隐藏？

问题关键代码如下：

```
// Index.ets
@Entry
@Component
struct NavigationPage {
  @Provide('pageInfos') pageInfos: NavPathStack = new NavPathStack();

  // 从子页返回根页面不会触发
  onPageShow(): void {
    console.info('NavigationPage onPageShow');
  }

  build() {
    Navigation(this.pageInfos) {
    };
  }
}
```

```
// PageOne.ets
@Entry
@Component
export struct PageOne {
  @Consume('pageInfos') pageInfos: NavPathStack;

  // onPageShow不会被触发
  onPageShow(): void {
    console.info('NavDestination PageOne onPageShow');
  }

  build() {
    NavDestination() {
    };
  }
}
```

#### 背景知识

*   onPageShow和onPageHide仅在router路由页面每次显示隐藏时触发。其他自定义组件生命周期无法触发。
*   Navigation是路由导航的根视图容器，一般作为页面（@Entry）的根容器，NavDestination是Navigation子页面的根容器。
*   @ohos.arkui.observer (无感监听)提供UI组件行为变化的无感监听能力。可通过uiObserver.on('navDestinationSwitch')监听Navigation的页面切换事件。

#### 解决方案

Navigation组件通常被用作Page页面的根容器，它内部默认包含标题栏、内容区域和工具栏。在内容区域中，默认情况下，首页会展示导航内容（即Navigation的子组件），而子页面则展示NavDestination的子组件。当从首页跳转至子页面时，实际打开的是NavDestination组件，而不是一个router页面，因此不会触发应用页面特有的onPageShow和onPageHide生命周期方法。

*   **场景一**：监听NavDestination组件的显示和隐藏。
    
    可以使用onShown事件和onHidden事件来监听NavDestination组件的显示和隐藏。示例可参考NavDestination生命周期时序。
    
*   **场景二**：监听Navigation首页的显示和隐藏。
    
    在Navigation不使用hideNavBar隐藏导航栏的场景下，监听Navigation的onNavBarStateChange事件。在回调函数中，如果变量isVisible为true，表明首页正在显示。
    
    ```
    @Entry
    @Component
    struct NavBarStateChangePage {
      pageInfos: NavPathStack = new NavPathStack();
    
      @Builder
      pageMap() {
        PageB();
      }
    
      build() {
        Navigation(this.pageInfos) {
          Column() {
            Button('跳转NavDestination页面')
              .onClick(() => {
                this.pageInfos.pushPath({ name: 'PageB' });
              });
          };
        }.navDestination(this.pageMap)
        .onNavBarStateChange((isVisible: boolean) => {
          if (isVisible) {
            console.info('Navigation显示');
          } else {
            console.info('Navigation隐藏');
          }
        });
      }
    }
    
    @Component
    struct PageB {
      pageInfos: NavPathStack = new NavPathStack();
    
      build() {
        NavDestination() {
          Button('返回Navigation')
            .onClick(() => {
              this.pageInfos.pop();
            });
        }.onReady((ctx: NavDestinationContext) => {
          this.pageInfos = ctx.pathStack;
        });
      }
    }
    ```
    

*   **场景三**：监听首页和子页的显示和隐藏。
    
    在首页的aboutToAppear函数中使用无感监听uiObserver.on('navDestinationSwitch')监听页面切换。回调函数的入参为NavDestinationSwitchInfo，可以根据from和to的信息判断隐藏和显示的页面。
    
    ```
    import { uiObserver } from '@kit.ArkUI';
    
    @Entry
    @Component
    struct NavDestinationSwitchPage {
      pageInfos: NavPathStack = new NavPathStack();
    
      aboutToAppear(): void {
        // 监听navigation页面切换事件
        uiObserver.on('navDestinationSwitch', this.getUIContext(), (switchInfo) => {
          // 可根据from和to判断显隐的页面，类型为NavDestinationInfo表示子页，为NavBar表示Navigation页面
          console.info(`from ${JSON.stringify(switchInfo.from)} -> to ${JSON.stringify(switchInfo.to)}`);
        });
      }
    
      aboutToDisappear() {
        uiObserver.off('navDestinationSwitch', this.getUIContext()); // 取消监听
      }
    
      @Builder
      pageMap() {
        PageA();
      }
    
      build() {
        Navigation(this.pageInfos) {
          Column() {
            Button('跳转NavDestination页面')
              .onClick(() => {
                this.pageInfos.pushPath({ name: 'PageA' });
              });
          };
        }.navDestination(this.pageMap);
      }
    }
    
    @Component
    struct PageA {
      pageInfos: NavPathStack = new NavPathStack();
    
      build() {
        NavDestination() {
          Button('返回Navigation')
            .onClick(() => {
              this.pageInfos.pop();
            });
        }.onReady((ctx: NavDestinationContext) => {
          this.pageInfos = ctx.pathStack;
        });
      }
    }
    ```
    

#### 常见FAQ

Q：Navigation子页面A跳转到子页面B后，重新返回页面A，如何监听A页面重新展示了？

A：onActive在NavDestination处于激活态（处于栈顶可操作，且上层无特殊组件遮挡）时，触发该回调。当重新返回A页面后会触发onActive函数。