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

#### 问题现象

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

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/e4/v3/CDydxzdgSpmGIAlc9pyjLQ/zh-cn_image_0000002633439122.png?HW-CC-KV=V1&HW-CC-Date=20260709T133933Z&HW-CC-Expire=86400&HW-CC-Sign=AA0DFA2D9B3AEEAD52ECE48D503F7879944A20AC7C7D3E703A4CEB09A43027BB "点击放大")

#### 背景知识

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

#### 解决方案

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

*   **查看组件自身是否有属性可以控制（Menu自身属性）**。
    
    经查看官方文档，[Menu](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-popup-and-menu-components-menu)无对应属性控制是否显示。
    
*   **让组件隐藏（Menu组件本身）**。
    
    通过[显隐控制](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-universal-attributes-visibility)可知，在Menu数据为空时，设置Menu组件visibility属性为None即可。注意此处不可设置为Hidden。此方法隐藏的原因是绑定的Builder组件为空没有内容可显示，因此点击无显示。参与布局会导致Builder内容可见，无法隐藏，因此只能使用None无法使用Hidden。同理可使用if让Builder内组件不参与组件树构建，不生成内容。
    
    | 
    名称
    
     | 
    
    描述
    
     |
    | :-- | :-- |
    | 
    
    Hidden
    
     | 
    
    隐藏，但参与布局进行占位。
    
     |
    | 
    
    Visible
    
     | 
    
    显示。
    
     |
    | 
    
    None
    
     | 
    
    隐藏，但不参与布局，不进行占位。
    
     |
    
*   **组件不触发Menu显示事件（被绑定Menu的组件）**。
    
    禁用控制：通过[禁用控制](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-universal-attributes-enable)可知，设置enabled为false即可阻止交互事件，因此设置绑定Menu事件的组件的enabled为false。
    
    ![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/56/v3/837-QvsMSze-dyr4AiT2GA/note_3.0-zh-cn.png?HW-CC-KV=V1&HW-CC-Date=20260709T133933Z&HW-CC-Expire=86400&HW-CC-Sign=875620F21C10BF3CEC14F40775EE4BD1B99D57A9C26A734CF232813F205A483C)
    
    此时该组件进入不可交互状态，不会响应[点击事件](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-universal-events-click)、[触摸事件](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-universal-events-touch)、[拖拽事件](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-universal-events-drag-drop)、[按键事件](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-universal-events-key)、[焦点事件](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-universal-focus-event)和[鼠标事件](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-universal-mouse-key)。**同时组件UI会发生变化**。
    
    触摸测试控制：通过[触摸测试控制](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-universal-attributes-hit-test-behavior)可知，将组件触摸测试拦截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：
        
        ![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/b1/v3/GXJD-ve5R6ewq5vhBgkcSw/zh-cn_image_0000002663798967.png?HW-CC-KV=V1&HW-CC-Date=20260709T133933Z&HW-CC-Expire=86400&HW-CC-Sign=4B26321AB0F365E4FB81B067051FD98493CD069096AB2D50D92C12FF8A6CF4F9 "点击放大")
        
    *   visibility = Hidden：
        
        ![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/e3/v3/t_JTWsBHTjCZ9WtjQQRdHA/zh-cn_image_0000002633599772.png?HW-CC-KV=V1&HW-CC-Date=20260709T133933Z&HW-CC-Expire=86400&HW-CC-Sign=2E5C39897B1FA5C1672C9E1C3D7FEBFBC3ADE20DE1E2B51B522EC65CD90019AF "点击放大")
        
    *   visibility = None：
        
        ![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/58/v3/KEoMdwl2QHOTGBGo_Le-nA/zh-cn_image_0000002663799007.png?HW-CC-KV=V1&HW-CC-Date=20260709T133933Z&HW-CC-Expire=86400&HW-CC-Sign=8E2A29785662C45E86D8FDA504B0D68836E43A215F3A064A9ECA6EB1DFDAA60D "点击放大")
        
*   **禁用控制：**
    
    @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：
        
        ![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/d2/v3/jU1FnYS0TcWq2u8w2HoxaA/zh-cn_image_0000002633440070.png?HW-CC-KV=V1&HW-CC-Date=20260709T133933Z&HW-CC-Expire=86400&HW-CC-Sign=E901247F05B2B66B34F6CA5F4553250FF1DAED2F8ECF990121C3898E8526FC07 "点击放大")
        
    *   enabled = false：
        
        ![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/a8/v3/vPHTQPp3RoCiD01YD3u7_w/zh-cn_image_0000002633600014.png?HW-CC-KV=V1&HW-CC-Date=20260709T133933Z&HW-CC-Expire=86400&HW-CC-Sign=C77C4FCA825BF0186D63F07D59EEB3FD9C8A29C84F08FED7001B1A7D2101F2C2 "点击放大")
        
*   **触摸测试控制：**
    
    @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：
        
        ![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/98/v3/ceLaTjrvRsW9KWl_rKPOdQ/zh-cn_image_0000002633600724.png?HW-CC-KV=V1&HW-CC-Date=20260709T133933Z&HW-CC-Expire=86400&HW-CC-Sign=7377182E9E934CF2FF4DCE967DF286A812B7D5E00D183D00FABD2116570E9F7E "点击放大")
        
    *   HitTestMode = None：
        
        ![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/21/v3/8Ldg130fQmCcYzB_IPKujQ/zh-cn_image_0000002663719991.png?HW-CC-KV=V1&HW-CC-Date=20260709T133933Z&HW-CC-Expire=86400&HW-CC-Sign=2B72C269F8893D3DE4B65DF099C44A63A136AAEAFE00B8625E8DF1CA1A1E5376 "点击放大")
        

#### 总结

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