【高心星出品】
文章目录
鸿蒙6.0应用开发------丰富多彩的菜单(Menu)
Menu是菜单接口,一般用于鼠标右键弹窗、点击弹窗等。具体用法请参考菜单控制。
使用bindContextMenu并设置预览图,菜单弹出时有蒙层,此时为模态。
使用bindMenu或bindContextMenu未设置预览图时,菜单弹出无蒙层,此时为非模态。
创建默认样式的菜单
菜单需要调用bindMenu接口来实现。bindMenu响应绑定组件的点击事件,绑定组件后手势点击对应组件后即可弹出。
TypeScript
Button('click for Menu')
.bindMenu([
{
value: 'Menu1',
action: () => {
hilog.info(0xFF00, 'DialogProject', 'handle Menu1 select');
}
}
])
代码逻辑走读:
- 创建一个按钮组件,按钮的文本显示为"click for Menu"。
- 使用
bindMenu方法将一个菜单绑定到按钮上。 - 菜单是一个数组,其中包含一个对象,该对象定义了一个菜单项。
- 菜单项的
value属性设置为"Menu1",表示菜单项的文本内容。 action属性是一个函数,当用户选择这个菜单项时,这个函数会被执行。- 在
action函数中,使用hilog.info方法记录一条日志,日志的标签为"DialogProject",日志级别为0xFF00,日志内容为"handle Menu1 select"。

创建自定义样式的菜单
当默认样式不满足开发需求时,可使用@Builder自定义菜单内容,通过bindMenu接口进行菜单的自定义。
使用@Builder自定义菜单内容
TypeScript
import { hilog } from '@kit.PerformanceAnalysisKit';
class Tmp {
// 请将$r('app.media.view_list_filled')替换为实际资源文件
public iconStr2: ResourceStr = $r('app.media.view_list_filled');
set(val: Resource) {
this.iconStr2 = val;
}
}
@Entry
@Component
export struct BuilderCustomMenuExample {
@State select: boolean = true;
// 请将$r('app.media.view_list_filled')替换为实际资源文件
private iconStr: ResourceStr = $r('app.media.view_list_filled');
private iconStr2: ResourceStr = $r('app.media.view_list_filled');
// 请将$r('app.string.copy')替换为实际资源文件,在本示例中该资源文件的value值为"复制"
private copy: ResourceStr = $r('app.string.copy');
// 请将$r('app.string.paste')替换为实际资源文件,在本示例中该资源文件的value值为"粘贴"
private paste: ResourceStr = $r('app.string.paste');
@Builder
SubMenu() {
Menu() {
MenuItem({ content: this.copy, labelInfo: 'Ctrl+C' })
MenuItem({ content: this.paste, labelInfo: 'Ctrl+V' })
}
}
@Builder
MyMenu() {
Menu() {
// 请将$r('app.string.menu_selection')替换为实际资源文件,在本示例中该资源文件的value值为"菜单选项"
// 请将$r('app.media.icon')替换为实际资源文件
// 请将$r('app.media.arrow_right_filled')替换为实际资源文件
MenuItem({ startIcon: $r('app.media.icon'), content: $r('app.string.menu_selection') })
MenuItem({ startIcon: $r('app.media.icon'), content: $r('app.string.menu_selection') }).enabled(false)
MenuItem({
startIcon: this.iconStr,
content: $r('app.string.menu_selection'),
endIcon: $r('app.media.arrow_right_filled'),
// 当builder参数进行配置时,表示与menuItem项绑定了子菜单。鼠标hover在该菜单项时,会显示子菜单。
builder: this.SubMenu
})
// 请将$r('app.string.menu_subtitle')替换为实际资源文件,在本示例中该资源文件的value值为"小标题"
MenuItemGroup({ header: $r('app.string.menu_subtitle') }) {
// 请将$r('app.string.menu_selection')替换为实际资源文件,在本示例中该资源文件的value值为"菜单选项"
MenuItem({ content: $r('app.string.menu_selection') })
.selectIcon(true)
.selected(this.select)
.onChange((selected) => {
hilog.info(0xFF00, 'DialogProject', 'menuItem select' + selected);
let str: Tmp = new Tmp();
str.set($r('app.media.icon'));
})
// 请将$r('app.string.menu_selection')替换为实际资源文件,在本示例中该资源文件的value值为"菜单选项"
// 请将$r('app.media.view_list_filled')替换为实际资源文件
// 请将$r('app.media.arrow_right_filled')替换为实际资源文件
MenuItem({
startIcon: $r('app.media.view_list_filled'),
content: $r('app.string.menu_selection'),
endIcon: $r('app.media.arrow_right_filled'),
builder: this.SubMenu
})
}
// 请将$r('app.string.menu_selection')替换为实际资源文件,在本示例中该资源文件的value值为"菜单选项"
// 请将$r('app.media.arrow_right_filled')替换为实际资源文件
MenuItem({
startIcon: this.iconStr2,
content: $r('app.string.menu_selection'),
endIcon: $r('app.media.arrow_right_filled')
})
}
}
build() {
// ...
}
}
代码逻辑走读:
- 导入模块 :
- 导入了
hilog用于日志记录,属于性能分析工具包。
- 导入了
- 定义辅助类 Tmp :
- 定义了一个类 Tmp,包含一个方法 set,用于设置
iconStr2属性。
- 定义了一个类 Tmp,包含一个方法 set,用于设置
- 组件定义 :
- 使用
@Entry和@Component装饰器定义了一个名为BuilderCustomMenuExample的组件。 - 定义了多个状态变量和资源字符串,用于初始化菜单项的属性。
- 使用
- 子菜单构建 :
- 定义了两个
@Builder方法SubMenu和MyMenu,用于构建子菜单和主菜单。 SubMenu构建一个简单的菜单,包含复制和粘贴菜单项。MyMenu构建一个复杂的菜单,包含多个菜单项、菜单项组和带子菜单的菜单项。
- 定义了两个
- 菜单项属性设置 :
- 使用
MenuItem和MenuItemGroup定义菜单项和菜单项组。 - 设置了菜单项的图标、启用状态、子菜单等属性。
- 使用
onChange事件处理器来响应菜单项的选择变化,并使用Tmp类来处理资源设置。
- 使用
- 构建方法 :
- 定义了
build方法,用于构建组件的 UI 结构。
- 定义了
使用bindMenu属性绑定组件
TypeScript
Button('click for Menu')
.bindMenu(this.MyMenu)

创建支持右键或长按的菜单
通过bindContextMenu接口自定义菜单,设置菜单弹出的触发方式,触发方式为右键或长按。使用bindContextMenu弹出的菜单项是在独立子窗口内的,可显示在应用窗口外部。
-
使用@Builder自定义菜单内容,与上文写法相同。
-
确认菜单的弹出方式,并使用bindContextMenu属性绑定组件。示例中为右键弹出菜单。
TypeScriptButton('Right-click for Menu') .bindContextMenu(this.MyMenu, ResponseType.RightClick)
菜单弹出时振动效果
菜单从API version 18开始支持振动效果。菜单弹出时,默认不振动。若希望菜单弹出时有振动效果,可以通过ContextMenuOptions的hapticFeedbackMode属性,设置菜单弹出时的振动模式。
-
只有一级菜单可配置弹出时振动效果。
-
仅当应用具备ohos.permission.VIBRATE权限,且用户启用了触感反馈时才会生效。开启触控反馈时,需要在工程的module.json5中配置声明权限的requestPermissions字段开启振动权限,配置如下:
cangjie"requestPermissions": [ { "name": "ohos.permission.VIBRATE", } ],
TypeScript
Button('click for Menu')
.id('click for Menu')
.bindMenu(this.MyMenu, { hapticFeedbackMode: HapticFeedbackMode.ENABLED})
菜单支持避让中轴
从API version 18起,菜单支持中轴避让功能。从API version 20开始,在2in1设备上默认启用(仅在窗口处于瀑布模式时产生避让)。开发者可通过ContextMenuOptions中的enableHoverMode属性,控制菜单是否启用中轴避让。
说明
- 如果菜单的点击位置在中轴区域,则菜单不会避让。
- 2in1设备上需同时满足窗口处于瀑布模式才会产生避让。
TypeScript
@Entry
@Component
export struct SupportAvoidCentralAxisMenuExample {
@State message: string = 'Hello World';
// 请在resources\base\element\string.json文件中配置name为'xxx',value为非空字符串的资源
@State upScreen: string =
this.getUIContext().getHostContext()?.resourceManager.getStringByNameSync('Upper_half_screen') as string;
@State middleAxle: string =
this.getUIContext().getHostContext()?.resourceManager.getStringByNameSync('Middle_axle') as string;
@State lowerScreen: string =
this.getUIContext().getHostContext()?.resourceManager.getStringByNameSync('Lower_half_screen') as string;
@State zone: string =
this.getUIContext().getHostContext()?.resourceManager.getStringByNameSync('zone') as string;
@State hoverModeStart: string =
this.getUIContext().getHostContext()?.resourceManager.getStringByNameSync('hoverMode_start') as string;
// 请将$r('app.media.startIcon')替换为实际资源文件
private iconStr: Resource = $r('app.media.startIcon');
@State index: number = 0;
@State arrayStr: Array<string> = [this.upScreen, this.middleAxle, this.lowerScreen];
@State enableHoverMode: boolean | undefined = true;
@State showInSubwindow: boolean = false;
@Builder
MyMenu1() {
Menu() {
// 请将$r('app.string.menu_selection')替换为实际资源文件,在本示例中该资源文件的value值为"菜单选项"
MenuItem({ startIcon: this.iconStr, content: $r('app.string.menu_selection') })
MenuItem({ startIcon: this.iconStr, content: $r('app.string.menu_selection') })
MenuItem({ startIcon: this.iconStr, content: $r('app.string.menu_selection') })
MenuItem({ startIcon: this.iconStr, content: $r('app.string.menu_selection') })
}
}
@State isShow: boolean = false;
build() {
NavDestination() {
Column({ space: 5 }) {
Button(this.zone + this.arrayStr[this.index])
.onClick(() => {
if (this.index < 2) {
this.index++
} else {
this.index = 0
}
})
Button(this.hoverModeStart + this.enableHoverMode)
.id('hoverMode_start')
.onClick(() => {
if (this.enableHoverMode === undefined) {
this.enableHoverMode = true
} else if (this.enableHoverMode === true) {
this.enableHoverMode = false
} else {
this.enableHoverMode = undefined
}
})
Button('Menu')
.fontWeight(FontWeight.Bold)
.bindMenu(this.MyMenu1(), {
enableHoverMode: this.enableHoverMode,
showInSubWindow: this.showInSubwindow
})
}
.height('100%')
.width('100%')
}
// ···
代码逻辑走读:
- 组件初始化 :
- 使用
@Entry和@Component装饰器定义了一个组件。 - 初始化了多个状态变量,如
message、upScreen、middleAxle、lowerScreen、zone、hoverModeStart、iconStr、index、arrayStr、enableHoverMode和showInSubwindow。
- 使用
- 资源加载 :
- 使用
this.getUIContext().getHostContext()?.resourceManager.getStringByNameSync()方法加载资源字符串,如Upper_half_screen、Middle_axle、Lower_half_screen和zone等。
- 使用
- 菜单构建 :
- 定义了一个名为
MyMenu1的@Builder方法,用于构建菜单,包含多个MenuItem。
- 定义了一个名为
- 界面构建 :
- 在
build方法中,使用NavDestination和Column组件构建界面。 - 添加了三个按钮,分别用于改变
index值、切换enableHoverMode状态和展示菜单。 - 按钮的点击事件通过
onClick方法定义,根据不同的条件改变状态。
- 在
- 状态管理 :
- 通过
@State装饰器管理组件的状态,如index、enableHoverMode和showInSubwindow等。 - 使用
onClick事件处理函数来更新状态,实现界面的动态交互。
- 通过

控制子窗菜单的事件透传
当菜单在子窗口中弹出时,默认情况下,菜单周围的事件会传递至所在窗口。从API version 20开始,开发者可通过ContextMenuOptions的modalMode属性设置子菜单弹出时的模态模式,以控制菜单周围事件是否传递。将modalMode设置为ModalMode.TARGET_WINDOW时,菜单周围的事件将不再传递,菜单下方的控件也不会响应事件。
TypeScript
@Entry
@Component
export struct EventTransSubWindowMenuExample {
build() {
NavDestination() {
Column() {
}
.id('click')
.bindContextMenu(this.contextMenuBuilder, ResponseType.RightClick, {
modalMode: ModalMode.TARGET_WINDOW
})
.onClick(() => {
this.getUIContext().getPromptAction().showToast({
message: 'Clicked!'
})
})
.width('100%')
.height('100%')
}
// ...
}
@Builder
bindMenuBuilder() {
Menu() {
MenuItem({ content: 'bindMenu item' }) {
}
}
}
@Builder
contextMenuBuilder() {
Menu() {
MenuItem({ content: 'contextMenu item' }) {
}
}
}
}
代码逻辑走读:
- 组件定义 :使用
@Entry和@Component装饰器定义了一个名为EventTransSubWindowMenuExample的组件。 - 构建方法 :
build()方法是组件的核心,用于定义组件的UI结构。 - 导航目的地 :在
build()方法中,使用NavDestination创建了一个导航目的地。 - 列组件 :在
NavDestination内部,使用Column创建了一个列组件。 - 绑定上下文菜单 :通过
.bindContextMenu()方法,将contextMenuBuilder方法生成的菜单绑定到列组件上,响应右键点击事件,并指定模态窗口模式为目标窗口。 - 点击事件 :使用
.onClick()方法,为列组件绑定了一个点击事件处理函数,当点击时,显示一个提示消息。 - 样式设置:设置列组件的宽度和高度为100%。
- 菜单构建 :定义了两个菜单构建器方法
bindMenuBuilder和contextMenuBuilder,分别用于生成绑定菜单和上下文菜单。 - 菜单项 :在
bindMenuBuilder和contextMenuBuilder方法中,使用Menu和MenuItem定义了菜单项内容。

基于绑定组件指定位置弹出菜单
菜单从API version 20开始支持基于绑定组件在指定位置弹出。通过设置水平与垂直偏移量,控制菜单相对于绑定组件左上角的弹出位置。与单独使用offset接口不同,此方法可使菜单覆盖显示在绑定组件上。需要指定弹出位置时,可使用ContextMenuOptions的anchorPosition属性进行设置。
说明
- 当菜单处于预览状态时,设定的定位偏移量将无法生效。
- 预设的placement对齐参数将不再生效。
- 叠加offset参数的偏移量,最终确定菜单的精确显示位置。
- 当水平与垂直偏移量均设为负值时,菜单以绑定组件左下角为基准点进行显示。
- 当水平或垂直偏移量存在负值时,组件将以绑定组件的左上角为定位基准点,通过叠加偏移量参数实现反向偏移。
TypeScript
@Entry
@Component
export struct BindComponentMenuExample {
@Builder
MenuBuilder() {
Column() {
Menu() {
MenuItemGroup() {
// 请将$r('app.media.app_icon')替换为实际资源文件
MenuItem({ startIcon: $r('app.media.app_icon'), content: 'Select Mixed Menu 1', labelInfo: '' })
MenuItem({ startIcon: $r('app.media.app_icon'), content: 'Select Mixed Menu 2', labelInfo: '' })
MenuItem({ startIcon: $r('app.media.app_icon'), content: 'Select Mixed Menu 3', labelInfo: '' })
}
}
}
}
build() {
NavDestination() {
Column() {
Text()
.borderRadius(10)
.width(200)
.height(150)
.borderWidth(1)
.backgroundColor(Color.White)
.borderColor(Color.Red)
.margin({ top: 200, left: 125 })
.bindContextMenu(this.MenuBuilder, ResponseType.RightClick, {
anchorPosition: { x: 45, y: 50 },
})
}
.alignItems(HorizontalAlign.Start)
.width('100%')
.height('100%')
.backgroundColor('#F5F5F5')
}
// ...
}
}
代码逻辑走读:
- 组件定义 :使用
@Entry和@Component装饰器定义了一个名为BindComponentMenuExample的组件。 - 菜单构建器 :定义了一个
MenuBuilder方法,用于构建一个菜单,菜单包含一个MenuItemGroup,每个菜单项使用MenuItem定义,包含图标、内容和标签信息。 - 文本框构建 :在
build方法中,创建了一个NavDestination,内部包含一个Column,其中一个Text元素被绑定到MenuBuilder生成的菜单,通过bindContextMenu方法绑定右键点击事件。 - 样式设置 :为
Text元素设置了边框圆角、宽度、高度、边框宽度、背景色和边框颜色等样式属性,并通过margin设置了位置。 - 布局设置 :
Column元素设置了水平对齐方式、宽度、高度和背景色,以确保文本框在布局中的位置和样式。
