目录

解决bindContextMenu长按菜单关闭时的事件穿透问题

问题现象

当使用bindContextMenu为组件A配置长按菜单时,菜单弹出后,如果点击菜单区域外的任意位置可以关闭菜单,但组件A(或其他区域外的组件)的点击事件也会被触发。

效果预览

背景知识

解决方案

为解决点击菜单区域外时事件穿透问题,当菜单显示时,在页面最外层组件上添加hitTestBehavior.Block属性以阻止事件传播。同时,在最外层组件的点击事件回调方法中加入一个if条件判断,判断是否是处于菜单显示的情况,若是则直接返回,不处理后面的点击逻辑。

import { PromptAction } from '@kit.ArkUI';

@Entry
@Component
struct BindContextMenuIndex {
  @State menuShow: boolean = false;
  @State flag: boolean = false;
  promptAction: PromptAction = new PromptAction();

  @Builder
  menu() {
    Menu() {
      MenuItem({ startIcon: $r('app.media.startIcon'), content: '菜单1' });
      MenuItem({ startIcon: $r('app.media.startIcon'), content: '菜单2' });
      MenuItem({ startIcon: $r('app.media.startIcon'), content: '菜单3' });
    };
  }

  build() {
    Column() {
      Text('第一层')
        .height('150vp');
      Column() {
        Text('第二层')
          .height('150vp');
        Column() {
          Text('第三层的兄弟层1')
            .height('150vp');
        }
        .width('100%')
        .backgroundColor('#0D5AF5')
        .onClick(() => {
          this.promptAction.showToast({ message: '第三层的兄弟层1' });
        })
        // 长按手势,将控制菜单显隐的值修改为true
        .gesture(LongPressGesture().onAction(() => {
          this.menuShow = true;
          this.flag = true;
        }))
        // 绑定菜单
        .bindContextMenu(!!this.menuShow, this.menu(), {
          onDisappear: () => {
            this.menuShow = false;
          }
        });

        Column() {
          Text('第三层的兄弟层2')
            .height('150vp');
        }
        .width('100%')
        .backgroundColor('#0D85F5')
        .onClick(() => {
          this.promptAction.showToast({ message: '第三层的兄弟层2' });
        });
      }
      .width('100%')
      .height('100%')
      .backgroundColor('#65BA5F')
      .onClick(() => {
        this.promptAction.showToast({ message: '第二层' });
      });
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#ECA724')
    .onClick(() => {
      console.info('onClick');
      if (this.flag) {
        this.flag = false;
        return;
      }
      this.promptAction.showToast({ message: '第一层' });
    })
    // 阻止事件穿透
    .hitTestBehavior(
      this.menuShow ?
      HitTestMode.Block :
      HitTestMode.Default
    );
  }
}