# 如何解决Menu组件在数据为空时依旧显示的问题

#### 问题现象

当Menu组件内数据为空时，点击蓝色的菜单按钮，左下角依旧弹出一个白色圆点，如下图所示。该现象符合组件设计逻辑，当展示组件内容为空时，仍然弹出空白的Menu组件容器。但如果需求要求菜单为空时不显示菜单，则该现象不符合要求。

#### 背景知识

*   Menu是菜单接口，一般用于鼠标右键弹窗、点击弹窗等。具体用法请参考菜单控制。
*   当前Menu组件作为一个绑定交互组件，被应用在各种地方，例如历史记录、账号等。目前Menu当其中的信息为空时，会展示一个空白的交互框提示用户无信息，但若应用设计方面决定不展示，则可参考如下实现。
    *   显隐控制：通过配置visibility的不同值，实现不同的显隐控制效果。
    *   禁用控制：通过enabled设置按钮可交互性。
    *   触摸测试控制：设置组件的触摸测试类型。在ArkUI开发框架中，处理触屏事件时，会在触屏事件触发前进行按压点与组件区域的触摸测试，以收集需响应触屏事件的组件。基于测试结果，框架会分发相应的触屏事件。hitTestBehavior属性用于设置不同的触摸测试响应模式，影响触摸测试收集结果及后续触屏事件分发。

#### 解决方案

使用bindMenu，让Menu为空时不显示可以通过如下三方面入手（其他组件也可类似分析）：

*   **查看组件自身是否有属性可以控制（Menu自身属性）**。
    
    经查看官方文档，Menu无对应属性控制是否显示。
    
*   **让组件隐藏（Menu组件本身）**。
    
    通过显隐控制可知，在Menu数据为空时，设置Menu组件visibility属性为None即可。注意此处不可设置为Hidden。此方法隐藏的原因是绑定的Builder组件为空没有内容可显示，因此点击无显示。参与布局会导致Builder内容可见，无法隐藏，因此只能使用None无法使用Hidden。同理可使用if让Builder内组件不参与组件树构建，不生成内容。
    
    | 名称 | 描述 |
    | --- | --- |
    | Hidden | 隐藏，但参与布局进行占位。 |
    | Visible | 显示。 |
    | None | 隐藏，但不参与布局，不进行占位。 |
    
*   **组件不触发Menu显示事件（被绑定Menu的组件）**。
    
    禁用控制：通过禁用控制可知，设置enabled为false即可阻止交互事件，因此设置绑定Menu事件的组件的enabled为false。
    
    此时该组件进入不可交互状态，不会响应点击事件、触摸事件、拖拽事件、按键事件、焦点事件和鼠标事件。**同时组件UI会发生变化**。
    
    触摸测试控制：通过触摸测试控制可知，将组件触摸测试拦截hitTestBehavior设置为HitTestMode.None，即可使当前组件不响应触摸事件，同时不影响同位置不同层级组件的点击响应。
    

使用三种方法的实现代码样例与效果如下：

*   **组件隐藏****：**
    
    ```
    @Entry
    @Component
    struct VisibilitySolution {
      @State visibilityParam: Visibility = Visibility.Visible;
    
      @Builder
      MyMenu() {
        Menu()
          .visibility(this.visibilityParam);
      }
    
      build() {
        Column({ space: 30 }) {
          Button('菜单')
            .bindMenu(this.MyMenu)
            .margin({ top: 16, bottom: 16 });
          Button('visibility = ' + this.visibilityParam)
            .onClick(() => {
              this.visibilityParam += 1;
              if (this.visibilityParam === 3) {
                this.visibilityParam = 0;
              }
            });
          Blank().height(40);
        }
        .width('100%')
        .height('100%')
        .justifyContent(FlexAlign.Center)
        .backgroundColor('#f1f3f5')
        .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM]);
      }
    }
    ```
    
    以下对应三种效果图：
    
    *   visibility = Visible：
        
    *   visibility = Hidden：
        
    *   visibility = None：
        
*   **禁用控制：**
    
    ```
    @Entry
    @Component
    struct EnableSolution {
      @State enabledParam: boolean = true;
    
      @Builder
      MyMenu() {
        Menu();
      }
    
      build() {
        Column({ space: 30 }) {
          Button('菜单')
            .bindMenu(this.MyMenu)
            .enabled(this.enabledParam)
            .margin({ top: 16, bottom: 16 });
          Button('enabled = ' + this.enabledParam)
            .onClick(() => {
              this.enabledParam = !this.enabledParam;
            });
          Blank().height(40);
        }
        .width('100%')
        .height('100%')
        .justifyContent(FlexAlign.Center)
        .backgroundColor('#f1f3f5')
        .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM]);
      }
    }
    ```
    
    以下是两种效果图：
    
    *   enabled = true：
        
    *   enabled = false：
        
*   **触摸测试控制：**
    
    ```
    @Entry
    @Component
    struct HitTestBehaviorSolution {
      @State hitTestBehaviorParam: HitTestMode = HitTestMode.Default;
    
      @Builder
      MyMenu() {
        Menu();
      }
    
      build() {
        Column({ space: 30 }) {
          Button('菜单')
            .bindMenu(this.MyMenu)
            .hitTestBehavior(this.hitTestBehaviorParam)
            .margin({ top: 16, bottom: 16 });
          Button('HitTestMode = ' + this.hitTestBehaviorParam)
            .onClick(() => {
              if (this.hitTestBehaviorParam === HitTestMode.Default) {
                this.hitTestBehaviorParam = HitTestMode.None;
              } else {
                this.hitTestBehaviorParam = HitTestMode.Default;
              }
            });
          Blank().height(40);
        }
        .width('100%')
        .height('100%')
        .justifyContent(FlexAlign.Center)
        .backgroundColor('#f1f3f5')
        .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM]);
      }
    }
    ```
    
    以下是两种效果图：
    
    *   HitTestMode = Default：
        
    *   HitTestMode = None：
        

#### 总结

对于通过交互显示组件的问题，可以按照定位思路从三方面出发，首先查看显示组件自身是否有单独属性可以处理，其次就是使用通用的属性visibility来处理显隐问题，最后可屏蔽交互事件，防止触发组件显示。