文档管理中心

如何实现点击Menu组件菜单选项弹出自定义弹框

问题现象

如何实现点击Menu组件菜单选项弹出自定义弹框?要求如下:

  1. 点击屏幕任意位置显示菜单列表。
  2. 点击菜单某个选项后,首先隐藏当前显示的菜单列表,随后展示对应的自定义弹窗。

效果预览

背景知识

  • @ohos.promptAction:创建并显示文本提示框、对话框和操作菜单。
  • bindContextMenu:给组件绑定菜单,菜单的显隐通过控制绑定的isShown触发。
  • ComponentContent:ComponentContent表示组件内容的实体封装,其对象支持在非UI组件中创建与传递,便于开发者对弹窗类组件进行解耦封装。
  • onChange:当选中状态发生变化时,触发该回调。只有手动触发且MenuItem状态改变时才会触发onChange回调。

解决方案

实现思路如下:

  1. 在entry/src/main/ets/entryability/EntryAbility.ets文件的onWindowStageCreate中存储下windowStage。
    onWindowStageCreate(windowStage: window.WindowStage): void {
      // onWindowStageCreate中存储下windowStage
      AppStorage.setOrCreate('windowStage', windowStage);
    
      windowStage.loadContent('pages/Index', (err) => {
        if (err.code) {
          hilog.error(DOMAIN, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err));
          return;
        }
        hilog.info(DOMAIN, 'testTag', 'Succeeded in loading the content.');
      });
    }
  2. 使用ComponentContent和@Builder构建可复用的弹窗组件,弹窗包含文本和关闭按钮,通过promptAction控制显示/隐藏,通过Params类传递弹窗内容。
    import { ComponentContent } from '@ohos.arkui.node';
    import { window } from '@kit.ArkUI';
    export class Params {
      text: string = '';
      close?: () => void;
    
      constructor(text: string) {
        this.text = text;
      }
    }
    
    export  const uiContext = (AppStorage.get('windowStage') as window.WindowStage).getMainWindowSync().getUIContext();
    export  const contentNode = new ComponentContent(uiContext, wrapBuilder(buildText), new Params('这是一个弹框'));
    export  const promptAction = uiContext.getPromptAction();
    AppStorage.setOrCreate('isHide',false);
    // 可复用的弹窗组件,弹窗包含文本和关闭按钮,通过promptAction控制显示/隐藏,通过Params类传递弹窗内容。
    @Builder
    function buildText(params: Params) {
      Column() {
        Text(params.text)
          .fontSize(22)
        Button('关闭弹窗')
          .onClick(() => {
            promptAction.closeCustomDialog(contentNode);
            AppStorage.setOrCreate('isHide',false);
          })
      }
      .borderRadius(10)
      .backgroundColor('#FFF0F0F0')
      .width('80%')
      .height(200)
      .alignItems(HorizontalAlign.Center)
      .justifyContent(FlexAlign.SpaceAround)
    }
  3. 使用MyMenuComponent结构体封装菜单逻辑,通过bindContextMenu绑定自定义菜单模板,点击事件记录坐标并控制菜单显示状态(isHide),菜单位置根据点击坐标动态计算偏移量。
    @Component
    export struct MyMenuComponent {
      @StorageLink ('isHide') isHide:boolean = false;
      private positionX: number = 0;
      private positionY: number = 0;
      @BuilderParam customMenu: () => void = this.customBuilder;
    
      @Builder
      customBuilder() {
      }
    
      build() {
        Column() {
          Row() {
          }.width('0%')
          .height('0%')
          .bindContextMenu(this.isHide, this.customMenu,
            {
              placement: Placement.BottomRight,
              // 根据点击屏幕位置设置菜单偏移
              offset: {
                x: this.positionX,
                y: this.positionY
              }
            })
    
          Column() {
            Text('长按屏幕显示菜单')
              .fontSize(30)
          }
          .justifyContent(FlexAlign.Center)
          .alignItems(HorizontalAlign.Center)
          .width('100%')
          .height('100%')
          .onClick((event?: ClickEvent) => {
            // 修改位置信息
            if (!this.isHide) {
              this.positionX = event!.displayX;
              this.positionY = event!.displayY;
              this.isHide = true;
            } else {
              this.isHide = false;
            };
          })
        }
      }
    }
  4. 菜单项通过onChange事件处理选择逻辑。
    import { BusinessError } from '@kit.BasicServicesKit';
    import { MyMenuComponent } from './MyMenuComponent';
    import {promptAction,contentNode}from './buildText';
    
    @Entry
    @Component
    struct Index {
      @Builder
      MyMenu() {
        Menu() {
          MenuItem({ content: '菜单选项1' })
          MenuItem({ content: '菜单选项2' })
          MenuItem({ content: '菜单选项3-点击弹窗openCustomDialog' })
            .onChange((selected: boolean) => {
              if (selected) {
                try {
                  promptAction.openCustomDialog(contentNode, { autoCancel: false });
                } catch (error) {
                  let message = (error as BusinessError).message;
                  let code = (error as BusinessError).code;
                  console.error(`OpenCustomDialog args error code is ${code}, message is ${message}`);
                }
                ;
              }
            })
        }
      }
    
      build() {
        Column() {
          MyMenuComponent({
            customMenu: this.MyMenu.bind(this)
          })
        }
      }
    }
在 FAQ 中进行搜索
请输入您想要搜索的关键词