如何拦截页面跳转
========

#### 问题现象

Navigation如何实现侧滑手势拦截与路由跳转前拦截？

#### 背景知识

*   [Navigation](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-basic-components-navigation)：路由导航的根视图容器。
*   [NavDestination](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-basic-components-navdestination)：作为子页面的根容器，用于显示Navigation的内容区。
*   [onBackPressed](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-basic-components-navdestination#onbackpressed10)：当与Navigation绑定的页面栈中存在内容时，此回调生效。当点击返回键时，触发该回调。返回值为true时，表示重写返回键逻辑，返回值为false时，表示回退到上一个页面。
*   [setInterception](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-basic-components-navigation#setinterception12)：设置Navigation页面跳转拦截回调。

#### 解决方案

*   **方案一：通过NavDestination组件的onBackPressed回调对返回事件进行拦截。**
    
    该方案支持拦截系统侧滑及NavDestination标题栏的返回事件，但不支持拦截NavPathStack.pop()操作。参考代码如下：
    
    .onBackPressed(() => {
      _// 此处可添加拦截处理逻辑，然后return true放行_
      this.getUIContext().getPromptAction().showDialog({
        message: '是否返回',
        alignment: DialogAlignment.Center,
        buttons: \[
          {
            text: '取消',
            color: '#0000FF'
          },
          {
            text: '确认',
            color: '#0000FF'
          }
        \]
      }).then((data: ShowDialogSuccessResponse) => {
        _// 操作菜单的响应结果,选中按钮在buttons数组中的索引，从0开始,第二个索引为1_
        _// 点击取消按钮，留在此页面_
        if (data.index === 0) {
          console.info(\`Succeeded in cancelling.\`);
        }
        _// 点击确认按钮，返回上层页面_
        if (data.index === 1) {
          _// 未传入拦截的参数正常返回_
          RouterManager.getInstance().pop();
          console.info(\`Succeeded in confirming.\`);
        }
      });
      return true;
    });
    
*   **方案二：通过封装自定义路由工具类实现。**
    
    1.  通过自定义路由管理单例，将所有的push事件都用路由管理单例执行，在需要拦截时统一处理。
    2.  在页面中绑定单例并实现跳转。
    
    参考代码如下：
    
    import { Prompt, ShowDialogSuccessResponse } from '@kit.ArkUI';
    
    _// 自定义路由管理工具类，以push和pop为例_
    export class RouterManager {
      private static instance: RouterManager;
      private navPathStack: NavPathStack | null = null;
    
      public static getInstance(): RouterManager {
        if (!RouterManager.instance) {
          RouterManager.instance = new RouterManager();
        }
        return RouterManager.instance;
      }
    
      public bindNavPathStack(stack: NavPathStack) {
        this.navPathStack = stack;
      }
    
      _// 跳转_
      public push(name: string, params?: object) {
        if (!this.navPathStack) {
          console.error('Failed to bind NavPathStack.');
          return;
        }
        _// 拦截处理_
        if (params && params\['data'\]) {
          const allow = this.dataInterception(params);
          if (allow) {
            _// 执行跳转_
            this.navPathStack.pushPathByName(name, params, false);
          } else {
            console.error(\`Failed to push.\`);
            Prompt.showToast({ message: '被拦截' });
            return;
          }
        } else {
          this.navPathStack.pushPathByName(name, null, false);
        }
      }
    
      _// 返回_
      public pop(params?: object) {
        _// 拦截处理_
        if (params && params\['data'\]) {
          const allow = this.dataInterception(params);
          if (allow) {
            _// 执行返回_
            this.navPathStack?.pop();
          } else {
            console.error(\`Failed to pop.\`);
            Prompt.showToast({ message: '被拦截' });
            return;
          }
        } else {
          _// 执行返回_
          this.navPathStack?.pop();
        }
      }
    
      _// 数据拦截_
      private dataInterception(param: object): boolean {
        if (param\['data'\] && param\['data'\] === '1') {
          return false;
        }
        return true;
      };
    
      _// 获取参数_
      public getParam(): object {
        let params = this.navPathStack?.getParamByIndex(this.navPathStack?.getAllPathName().length - 1) as object;
        return params;
      }
    }
    
*   **方案三：使用setInterception拦截Navigation页面跳转。**
    
    [setInterception](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-basic-components-navigation#setinterception12)的willShow回调和interception回调可实现页面跳转前路由拦截，具体使用开发者可参考[示例2（使用导航控制器方法）](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-basic-components-navigation#示例2使用导航控制器方法)和[示例17（使用新增导航控制器方法）](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-basic-components-navigation#示例17使用新增导航控制器方法)。
    

方案一和方案二的完整代码如下所示：

import { Prompt, ShowDialogSuccessResponse } from '@kit.ArkUI';

_// 自定义路由管理工具类，以push和pop为例_
export class RouterManager {
  private static instance: RouterManager;
  private navPathStack: NavPathStack | null = null;

  public static getInstance(): RouterManager {
    if (!RouterManager.instance) {
      RouterManager.instance = new RouterManager();
    }
    return RouterManager.instance;
  }

  public bindNavPathStack(stack: NavPathStack) {
    this.navPathStack = stack;
  }

  _// 跳转_
  public push(name: string, params?: object) {
    if (!this.navPathStack) {
      console.error('Failed to bind NavPathStack.');
      return;
    }
    _// 拦截处理_
    if (params && params\['data'\]) {
      const allow = this.dataInterception(params);
      if (allow) {
        _// 执行跳转_
        this.navPathStack.pushPathByName(name, params, false);
      } else {
        console.error(\`Failed to push.\`);
        Prompt.showToast({ message: '被拦截' });
        return;
      }
    } else {
      this.navPathStack.pushPathByName(name, null, false);
    }
  }

  _// 返回_
  public pop(params?: object) {
    _// 拦截处理_
    if (params && params\['data'\]) {
      const allow = this.dataInterception(params);
      if (allow) {
        _// 执行返回_
        this.navPathStack?.pop();
      } else {
        console.error(\`Failed to pop.\`);
        Prompt.showToast({ message: '被拦截' });
        return;
      }
    } else {
      _// 执行返回_
      this.navPathStack?.pop();
    }
  }

  _// 数据拦截_
  private dataInterception(param: object): boolean {
    if (param\['data'\] && param\['data'\] === '1') {
      return false;
    }
    return true;
  };

  _// 获取参数_
  public getParam(): object {
    let params = this.navPathStack?.getParamByIndex(this.navPathStack?.getAllPathName().length - 1) as object;
    return params;
  }
}
interface DataInterface {
  data: string;
}

@Entry
@Component
struct RouterManagerPage {
  pageInfo: NavPathStack = new NavPathStack();
  controller: TextAreaController = new TextAreaController();
  text: string = '';
  params: DataInterface = { data: this.text };

  @Builder
  pageMap(name: string) {
    if (name === 'SubPage') {
      SubPage();
    }
  }

  aboutToAppear(): void {
    RouterManager.getInstance().bindNavPathStack(this.pageInfo);
  }

  build() {
    Navigation(this.pageInfo) {
      Column({ space: 16 }) {
        TextArea({
          text: this.text,
          placeholder: 'input your word...',
          controller: this.controller
        })
          .width('80%')
          .onChange((value: string) => {
            this.text = value;
            this.params.data = this.text;
          });
        Button('push')
          .onClick(() => {
            RouterManager.getInstance().push('SubPage', this.params);
            console.info(\`Succeeded in getting text:${this.text}\`);
          });
      }
      .padding({top:32})
      .justifyContent(FlexAlign.Start)
      .width('100%')
      .height('100%');
    }
    .navDestination(this.pageMap)
    .width('100%')
    .height('100%');
  }
}
@Component
struct SubPage {
  build() {
    NavDestination() {
      Column({ space: 16 }) {
        Button('onBackPressed不支持使用pop()拦截返回。')
          .onClick(() => {
            _// 未传入拦截的参数_
            RouterManager.getInstance().pop();
          });
        Button('pop添加拦截功能')
          .onClick(() => {
            let param = { data: '1' } as DataInterface;
            _// 传入拦截参数_
            RouterManager.getInstance().pop(param);
          });
      }
      .padding({ top: 32 })
      .justifyContent(FlexAlign.Start)
      .width('100%')
      .height('100%');
    }
    .onBackPressed(() => {
      _// 此处可添加拦截处理逻辑，然后return true放行_
      this.getUIContext().getPromptAction().showDialog({
        message: '是否返回',
        alignment: DialogAlignment.Center,
        buttons: \[
          {
            text: '取消',
            color: '#0000FF'
          },
          {
            text: '确认',
            color: '#0000FF'
          }
        \]
      }).then((data: ShowDialogSuccessResponse) => {
        _// 操作菜单的响应结果,选中按钮在buttons数组中的索引，从0开始,第二个索引为1_
        _// 点击取消按钮，留在此页面_
        if (data.index === 0) {
          console.info(\`Succeeded in cancelling.\`);
        }
        _// 点击确认按钮，返回上层页面_
        if (data.index === 1) {
          _// 未传入拦截的参数正常返回_
          RouterManager.getInstance().pop();
          console.info(\`Succeeded in confirming.\`);
        }
      });
      return true;
    });
  }
}

效果图如下：

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/a0/v3/SYrTvdwpS46L5JNHjbaZMg/zh-cn_image_0000002658806751.png?HW-CC-KV=V1&HW-CC-Date=20260709T134112Z&HW-CC-Expire=86400&HW-CC-Sign=2C91A51DD7A5443D3B07A0AF141DD6A3BC64D664A3B4F57CCAD65FD66A8E28C2 "点击放大")

#### 常见FAQ

Q：@Entry装饰的页面如何实现返回拦截？

A：使用@Entry装饰的页面可通过[onBackPress](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-custom-component-lifecycle#onbackpress)实现返回拦截，返回true表示页面自己处理返回逻辑，不进行页面路由；返回false表示使用默认的路由返回逻辑，不设置返回值按照false处理。