开天辟地 HarmonyOS(鸿蒙) - 组件(弹出类): Menu(上下文菜单)

源码 https://github.com/webabcd/HarmonyDemo
作者 webabcd

开天辟地 HarmonyOS(鸿蒙) - 组件(弹出类): Menu(上下文菜单)

示例如下:

pages\component\flyout\MenuDemo.ets

/*
 * Menu - 上下文菜单
 *
 * bindContextMenu() - 为组件绑定一个上下文菜单(长按或右键)
 * bindMenu() - 为组件绑定一个上下文菜单(单击)
 * ContextMenu.close() - 关闭上下文菜单
 *
 * Menu, MenuItemGroup, MenuItem - 构造标准的上下文菜单的相关组件
 */

import { TitleBar } from '../../TitleBar'
import { LengthMetrics, SymbolGlyphModifier } from '@kit.ArkUI'

@Entry
@Component
struct ContextMenuDemo {

  build() {
    Column() {
      TitleBar()
      Tabs() {
        TabContent() { MySample1() }.tabBar('bindContextMenu()').align(Alignment.Top)
        TabContent() { MySample2() }.tabBar('bindMenu()').align(Alignment.Top)
        TabContent() { MySample3() }.tabBar('Menu, MenuItemGroup, MenuItem').align(Alignment.Top)
      }
      .scrollable(true)
      .barMode(BarMode.Scrollable)
      .layoutWeight(1)
    }
  }
}

@Component
struct MySample1 {

  @State message: string = ""
  @State isShown: boolean = false

  @Builder myMenu() {
    Column() {
      Button("button1").onClick(() => {

      })
      Divider()
      Button("button2").onClick(() => {

      })
    }
    .justifyContent(FlexAlign.SpaceEvenly)
    .width(200)
    .height(150)
  }

  build() {
    Column({space:10}) {
      Text(this.message)

      /*
       * bindContextMenu() - 为组件绑定一个上下文菜单(长按或右键)
       *   content - 自定义的菜单组件
       *   responseType - 菜单的弹出条件(ResponseType	枚举)
       *     LongPress, RightClick
       *   options - 选项(一个 ContextMenuOptions 对象)
       *     placement - 弹出菜单相对于绑定的组件的显示位置(Placement 枚举)
       *       Left, Right, Top, Bottom, TopLeft, TopRight, BottomLeft, BottomRight, LeftTop, LeftBottom, RightTop, RightBottom
       *     offset - 弹出菜单在当前位置的基础上的偏移距离
       *     enableArrow - 是否显示箭头
       *     arrowOffset - 箭头的偏移距离
       *     onAppear() - 完全弹出时的回调
       *     onDisappear() - 完全隐藏时的回调
       *     aboutToAppear() - 开始弹出时的回调
       *     aboutToDisappear() - 开始隐藏时的回调
       *     backgroundColor, backgroundBlurStyle - 背景颜色,背景的模糊效果
       *     transition - 菜单的显示消失的动画效果(TransitionEffect 对象)
       *       注:关于 TransitionEffect 的使用请参见 TransitionDemo.ets 中的说明
       *
       * ContextMenu.close() - 关闭上下文菜单
       */
      Text("Text1").fontSize(24).textAlign(TextAlign.Center)
        .width('100%').height(100).backgroundColor(Color.Yellow)
        .bindContextMenu(this.myMenu, ResponseType.LongPress, {
          placement: Placement.BottomLeft,
          offset: {
            x: 50,
            y: 20
          },
          enableArrow: true,
          arrowOffset: 100,
          onAppear: () => {
            // 开始弹出到完全弹出之间会有一段动画效果
            this.message = '完全弹出'
          },
          onDisappear: () => {
            // 开始隐藏到完全隐藏之间会有一段动画效果
            this.message = '完全隐藏'
          },
          aboutToAppear: () => {
            // 开始弹出到完全弹出之间会有一段动画效果
            this.message = '开始弹出'
          },
          aboutToDisappear: () => {
            // 开始隐藏到完全隐藏之间会有一段动画效果
            this.message = '开始隐藏'
          },
          backgroundColor: Color.Transparent,
          backgroundBlurStyle: BlurStyle.Thin,
          transition: TransitionEffect.asymmetric(
            // 菜单出现时的过渡动画效果
            TransitionEffect.SLIDE.animation({ duration: 1000 }),
            // 菜单消失时的过渡动画效果
            TransitionEffect.OPACITY.animation({ duration: 1000 })
          ),
        })

      /*
       * bindContextMenu() - 为组件绑定一个上下文菜单(长按或右键)
       *   isShown - 是否显示菜单
       *   content - 自定义的菜单组件
       *   options - 选项(一个 ContextMenuOptions 对象)
       */
      Text("Text2").fontSize(24).textAlign(TextAlign.Center)
        .width('100%').height(100).backgroundColor(Color.Yellow)
        .bindContextMenu(this.isShown, this.myMenu,{
          placement: Placement.BottomLeft,
          onDisappear: () => {
            this.isShown = false
          },
        })
      Button('显示 Text2 的上下文菜单').onClick(() => {
        this.isShown = !this.isShown
      })
    }
  }
}

@Component
struct MySample2 {

  @State message: string = ""

  @Builder myMenu() {
    Column() {
      Button("button1").onClick(() => {

      })
      Divider()
      Button("button2").onClick(() => {

      })
    }
    .justifyContent(FlexAlign.SpaceEvenly)
    .width(200)
    .height(150)
  }

  build() {
    Column({space:10}) {

      Text(this.message)

      /*
       * bindMenu() - 为组件绑定一个上下文菜单(单击)
       *   content - 自定义的菜单组件
       *   options - 选项(一个 MenuOptions 对象)
       *     showInSubWindow - 是否新开子窗口显示弹窗(比如小窗显示 app 时,如果新开子窗口显示弹窗,则这个弹窗会显示在 app 的所属窗口之外)
       *     placement - 弹出菜单相对于绑定的组件的显示位置(Placement 枚举)
       *       Left, Right, Top, Bottom, TopLeft, TopRight, BottomLeft, BottomRight, LeftTop, LeftBottom, RightTop, RightBottom
       *     offset - 弹出菜单在当前位置的基础上的偏移距离
       *     enableArrow - 是否显示箭头
       *     arrowOffset - 箭头的偏移距离
       *     onAppear() - 完全弹出时的回调
       *     onDisappear() - 完全隐藏时的回调
       *     aboutToAppear() - 开始弹出时的回调
       *     aboutToDisappear() - 开始隐藏时的回调
       *     backgroundColor, backgroundBlurStyle - 背景颜色,背景的模糊效果
       *     transition - 菜单的显示消失的动画效果(TransitionEffect 对象)
       *       注:关于 TransitionEffect 的使用请参见 TransitionDemo.ets 中的说明
       *
       * ContextMenu.close() - 关闭上下文菜单
       */
      Text("Text1").fontSize(24).textAlign(TextAlign.Center)
        .width('100%').height(100).backgroundColor(Color.Yellow)
        .bindMenu(this.myMenu, {
          showInSubWindow: false,
          placement: Placement.BottomLeft,
          offset: {
            x: 50,
            y: 20
          },
          enableArrow: true,
          arrowOffset: 100,
          onAppear: () => {
            // 开始弹出到完全弹出之间会有一段动画效果
            this.message = '完全弹出'
          },
          onDisappear: () => {
            // 开始隐藏到完全隐藏之间会有一段动画效果
            this.message = '完全隐藏'
          },
          aboutToAppear: () => {
            // 开始弹出到完全弹出之间会有一段动画效果
            this.message = '开始弹出'
          },
          aboutToDisappear: () => {
            // 开始隐藏到完全隐藏之间会有一段动画效果
            this.message = '开始隐藏'
          },
          backgroundColor: Color.Transparent,
          backgroundBlurStyle: BlurStyle.Thin,
          transition: TransitionEffect.asymmetric(
            // 菜单出现时的过渡动画效果
            TransitionEffect.SLIDE.animation({ duration: 1000 }),
            // 菜单消失时的过渡动画效果
            TransitionEffect.OPACITY.animation({ duration: 1000 })
          ),
        })

      /*
       * bindMenu() - 为组件绑定一个上下文菜单(单击)
       *   content - 除了像上面的例子那样指定一个自定义的菜单组件之外,还可以指定一个 MenuElement 对象数组
       *   options - 选项(一个 MenuOptions 对象)
       *     title - 菜单的标题(仅在 content 指定一个 MenuElement 对象数组时有效)
       *
       * MenuElement - 用于定义菜单项
       *   value - 菜单项的文本
       *   icon - 菜单项的图标
       *   symbolIcon - 菜单项的图标(如果同时设置了 icon 和 symbolIcon,则以 symbolIcon 为准)
       *     关于 SymbolGlyph 请参见 /component/text/SymbolGlyphDemo.ets 中的说明
       *   enabled - 是否可点击
       *   action - 点击后的回调
       */
      Text("Text2").fontSize(24).textAlign(TextAlign.Center)
        .width('100%').height(100).backgroundColor(Color.Yellow)
        .bindMenu([
          {
            value: 'menu 1',
            icon: $r('app.media.app_icon'),
            enabled: true,
            action: () => { }
          },
          {
            symbolIcon: new SymbolGlyphModifier($r('sys.symbol.ohos_wifi')).fontColor([Color.Green]),
            value: 'menu 2',
            enabled: false,
            action: () => { }
          },
        ], {
          title: "title",
          placement: Placement.BottomLeft,
        })
    }
  }
}

@Component
struct MySample3 {

  @State message: string = ''

  @Builder mySubMenu() {
    Menu() {
      MenuItem({ content: "item 1", })
      MenuItem({ content: "item 2", })
    }
  }

  /*
   * Menu - 标准的上下文菜单组件
   *   font() - 字体样式(size, weight, style, family)
   *   fontColor() - 字体颜色
   *   radius() - 圆角半径
   *   width() - 宽度
   *   menuItemDivider() - MenuItem 之间的分隔线(一个 DividerStyleOptions 对象)
   *     strokeWidth, color, startMargin, endMargin
   *   menuItemGroupDivider() - MenuItemGroup 之间的分隔线(一个 DividerStyleOptions 对象)
   *     strokeWidth, color, startMargin, endMargin
   *   subMenuExpandingMode() - 子菜单的展开模式(SubMenuExpandingMode 枚举)
   *     SIDE_EXPAND - 侧边通过弹窗打开
   *     EMBEDDED_EXPAND - 内嵌到主菜单展开
   *     STACK_EXPAND - 在主菜单上通过弹窗打开,同时主菜单做半透明效果
   *
   * MenuItemGroup - Menu 内的 MenuItem 分组
   *   header, footer - 组的 header 和 footer,可以指定一个字符串或自定义组件
   *
   * MenuItem - Menu 内的或 MenuItemGroup 内的菜单项(可以指定一个 MenuItemOptions 对象或一个自定义组件)
   *   startIcon, symbolStartIcon - 左侧图标
   *   endIcon, symbolEndIcon - 右侧图标
   *   content - 左侧内容
   *   labelInfo - 右侧内容
   *   contentFont(), contentFontColor() - 左侧内容的字体样式和字体颜色
   *   labelFont(), labelFontColor() - 右侧内容的字体样式和字体颜色
   *   onChange() - 选中状态发生变化时的回调
   */
  @Builder myMenu(subMenuExpandingMode:SubMenuExpandingMode) {
    Menu() {
      MenuItem({
        startIcon: $r("app.media.app_icon"),
        content: "item 1",
        labelInfo: "labelInfo",
        endIcon: $r("app.media.ic_settings"),
      })
        .contentFont({ size: 24, weight: 600 })
        .contentFontColor(Color.Green)
        .labelFont({ size: 24, weight: 600 })
        .labelFontColor(Color.Green)
        .onChange((selected: boolean) => {
          this.message = `item 1: ${selected}`
        })

      MenuItem({
        symbolStartIcon: new SymbolGlyphModifier($r('sys.symbol.ohos_wifi')).fontColor([Color.Green]),
        content: "item 2",
        labelInfo: "labelInfo",
        symbolEndIcon: new SymbolGlyphModifier($r('sys.symbol.ohos_trash')).fontColor([Color.Green]),
      })

      MenuItem({
        content: "item 3(有子菜单)",
        builder: () => this.mySubMenu()
      })

      MenuItemGroup({
        header: 'header',
        footer: 'footer'
      }) {
        MenuItem({ content: "item 1", })
        MenuItem({ content: "item 2", })
      }
    }
    .font({ size: 16, weight: 400 })
    .fontColor(Color.Orange)
    .radius(10)
    .width(300)
    .menuItemDivider({
      strokeWidth: LengthMetrics.vp(5),
      color: Color.Red,
      startMargin: LengthMetrics.vp(0),
      endMargin: LengthMetrics.vp(0),
    })
    .menuItemGroupDivider({
      strokeWidth: LengthMetrics.vp(5),
      color: Color.Blue,
      startMargin: LengthMetrics.vp(0),
      endMargin: LengthMetrics.vp(0),
    })
    .subMenuExpandingMode(subMenuExpandingMode)
  }

  build() {
    Column({space:10}) {

      Text(this.message)

      Text("Text1").fontSize(24).textAlign(TextAlign.Center)
        .width('100%').height(100).backgroundColor(Color.Yellow)
        .bindMenu(this.myMenu(SubMenuExpandingMode.SIDE_EXPAND))
        .bindContextMenu(this.myMenu(SubMenuExpandingMode.SIDE_EXPAND), ResponseType.LongPress)

      Text("Text2").fontSize(24).textAlign(TextAlign.Center)
        .width('100%').height(100).backgroundColor(Color.Yellow)
        .bindMenu(this.myMenu(SubMenuExpandingMode.EMBEDDED_EXPAND))
        .bindContextMenu(this.myMenu(SubMenuExpandingMode.EMBEDDED_EXPAND), ResponseType.LongPress)

      Text("Text3").fontSize(24).textAlign(TextAlign.Center)
        .width('100%').height(100).backgroundColor(Color.Yellow)
        .bindMenu(this.myMenu(SubMenuExpandingMode.STACK_EXPAND))
        .bindContextMenu(this.myMenu(SubMenuExpandingMode.STACK_EXPAND), ResponseType.LongPress)
    }
  }
}

源码 https://github.com/webabcd/HarmonyDemo
作者 webabcd

posted @ 2025-02-06 08:23  webabcd  阅读(119)  评论(0)    收藏  举报