• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 菜单控制(Menu)
2<!--Kit: ArkUI-->
3<!--Subsystem: ArkUI-->
4<!--Owner: @Armstrong15-->
5<!--Designer: @zhanghaibo0-->
6<!--Tester: @lxl007-->
7<!--Adviser: @HelloCrease-->
8
9Menu是菜单接口,一般用于鼠标右键弹窗、点击弹窗等。具体用法请参考[菜单控制](../reference/apis-arkui/arkui-ts/ts-universal-attributes-menu.md)。
10
11使用[bindContextMenu](../reference//apis-arkui/arkui-ts/ts-universal-attributes-menu.md#bindcontextmenu12)并设置预览图,菜单弹出时有蒙层,此时为模态。
12
13使用[bindMenu](../reference/apis-arkui/arkui-ts/ts-universal-attributes-menu.md#bindmenu11)或bindContextMenu未设置预览图时,菜单弹出无蒙层,此时为非模态。
14
15## 生命周期
16
17| 名称| 类型 | 说明 |
18| --- | --- | --- |
19| aboutToAppear  | () =>  void | 菜单显示动效前的事件回调。 |
20| onAppear | () =>  void | 菜单弹出时的事件回调。 |
21| aboutToDisappear | () =>  void | 菜单退出动效前的事件回调。 |
22| onDisappear  | () =>  void | 菜单消失时的事件回调。 |
23
24
25
26## 创建默认样式的菜单
27
28菜单需要调用bindMenu接口来实现。bindMenu响应绑定组件的点击事件,绑定组件后手势点击对应组件后即可弹出。
29
30```ts
31Button('click for Menu')
32  .bindMenu([
33    {
34      value: 'Menu1',
35      action: () => {
36        console.info('handle Menu1 select');
37      }
38    }
39  ])
40```
41
42![zh-cn_image_0000001562940565](figures/zh-cn_image_0000001562940565.png)
43
44## 创建自定义样式的菜单
45
46当默认样式不满足开发需求时,可使用[@Builder](../../application-dev/ui/state-management/arkts-builder.md)自定义菜单内容,通过bindMenu接口进行菜单的自定义。
47
48### 使用@Builder自定义菜单内容
49
50```ts
51class Tmp {
52  // $r('app.media.xxx')需要替换为开发者所需的图像资源文件。
53  iconStr2: ResourceStr = $r("app.media.view_list_filled");
54
55  set(val: Resource) {
56    this.iconStr2 = val;
57  }
58}
59
60@Entry
61@Component
62struct menuExample {
63  @State select: boolean = true;
64  private iconStr: ResourceStr = $r("app.media.view_list_filled");
65  private iconStr2: ResourceStr = $r("app.media.view_list_filled");
66
67  @Builder
68  SubMenu() {
69    Menu() {
70      MenuItem({ content: "复制", labelInfo: "Ctrl+C" })
71      MenuItem({ content: "粘贴", labelInfo: "Ctrl+V" })
72    }
73  }
74
75  @Builder
76  MyMenu() {
77    Menu() {
78      MenuItem({ startIcon: $r("app.media.icon"), content: "菜单选项" })
79      MenuItem({ startIcon: $r("app.media.icon"), content: "菜单选项" }).enabled(false)
80      MenuItem({
81        startIcon: this.iconStr,
82        content: "菜单选项",
83        endIcon: $r("app.media.arrow_right_filled"),
84        // 当builder参数进行配置时,表示与menuItem项绑定了子菜单。鼠标hover在该菜单项时,会显示子菜单。
85        builder: this.SubMenu
86      })
87      MenuItemGroup({ header: '小标题' }) {
88        MenuItem({ content: "菜单选项" })
89          .selectIcon(true)
90          .selected(this.select)
91          .onChange((selected) => {
92            console.info("menuItem select" + selected);
93            let Str: Tmp = new Tmp();
94            Str.set($r("app.media.icon"));
95          })
96        MenuItem({
97          startIcon: $r("app.media.view_list_filled"),
98          content: "菜单选项",
99          endIcon: $r("app.media.arrow_right_filled"),
100          builder: this.SubMenu
101        })
102      }
103
104      MenuItem({
105        startIcon: this.iconStr2,
106        content: "菜单选项",
107        endIcon: $r("app.media.arrow_right_filled")
108      })
109    }
110  }
111
112  build() {
113    // ...
114  }
115}
116
117```
118
119### 使用bindMenu属性绑定组件
120
121```ts
122Button('click for Menu')
123  .bindMenu(this.MyMenu)
124```
125
126![zh-cn_image_0000001511580924](figures/zh-cn_image_0000001511580924.png)
127
128## 创建支持右键或长按的菜单
129
130通过bindContextMenu接口自定义菜单,设置菜单弹出的触发方式,触发方式为右键或长按。使用bindContextMenu弹出的菜单项是在独立子窗口内的,可显示在应用窗口外部。
131
132- 使用@Builder自定义菜单内容,与上文写法相同。
133- 确认菜单的弹出方式,并使用bindContextMenu属性绑定组件。示例中为右键弹出菜单。
134
135  ```ts
136  Button('click for Menu')
137    .bindContextMenu(this.MyMenu, ResponseType.RightClick)
138  ```
139
140## 菜单弹出时振动效果
141
142菜单从API version 18开始支持振动效果。菜单弹出时,默认不振动。若希望菜单弹出时有振动效果,可以通过[ContextMenuOptions](../reference/apis-arkui/arkui-ts/ts-universal-attributes-menu.md#contextmenuoptions10)的hapticFeedbackMode属性,设置菜单弹出时的振动模式。
143
144- 只有一级菜单可配置弹出时振动效果。
145- 仅当应用具备ohos.permission.VIBRATE权限,且用户启用了触感反馈时才会生效。开启触控反馈时,需要在工程的module.json5中配置[声明权限](../security/AccessToken/declare-permissions.md)的requestPermissions字段开启振动权限,配置如下:
146
147  ```json
148  "requestPermissions": [
149    {
150      "name": "ohos.permission.VIBRATE",
151    }
152  ]
153  ```
154
155```ts
156  Button('click for Menu')
157    .bindContextMenu(this.MyMenu, ResponseType.RightClick, { hapticFeedbackMode: HapticFeedbackMode.ENABLED })
158```
159
160## 菜单支持避让中轴
161
162从API version 18起,菜单支持中轴避让功能。从API version 20开始,在2in1设备上默认启用(仅在窗口处于瀑布模式时产生避让)。开发者可通过[ContextMenuOptions](../reference/apis-arkui/arkui-ts/ts-universal-attributes-menu.md#contextmenuoptions10)中的enableHoverMode属性,控制菜单是否启用中轴避让。
163
164> **说明:**
165> - 如果菜单的点击位置在中轴区域,则菜单不会避让。
166> - 2in1设备上需同时满足窗口处于瀑布模式才会产生避让。
167
168```ts
169@Entry
170@Component
171struct Index {
172  @State message: string = 'Hello World';
173  // $r('app.media.startIcon')需要替换为开发者所需的图像资源文件。
174  private iconStr: Resource = $r('app.media.startIcon');
175  @State index: number = 0;
176  @State arrayStr: Array<string> = ['上半屏', '中轴', '下半屏'];
177  @State enableHoverMode: boolean | undefined = true;
178  @State showInSubwindow: boolean = false;
179  @State placement: Placement | undefined = undefined;
180
181  @Builder
182  MyMenu1() {
183    Menu() {
184      MenuItem({ startIcon: this.iconStr, content: '菜单选项' })
185      MenuItem({ startIcon: this.iconStr, content: '菜单选项' })
186      MenuItem({ startIcon: this.iconStr, content: '菜单选项' })
187      MenuItem({ startIcon: this.iconStr, content: '菜单选项' })
188    }
189  }
190
191  @State isShow: boolean = false;
192
193  build() {
194    RelativeContainer() {
195      Column() {
196        Button('区域:' + this.arrayStr[this.index])
197          .onClick(() => {
198            if (this.index < 2) {
199              this.index++
200            } else {
201              this.index = 0
202            }
203          })
204
205        Button('hoverMode开启:' + this.enableHoverMode)
206          .onClick(() => {
207            if (this.enableHoverMode == undefined) {
208              this.enableHoverMode = true
209            } else if (this.enableHoverMode == true) {
210              this.enableHoverMode = false
211            } else {
212              this.enableHoverMode = undefined
213            }
214          })
215
216        Button('MenuPlacement:' + this.placement)
217          .onClick(() => {
218            if (this.placement == undefined) {
219              this.placement = Placement.Bottom
220            } else if (this.placement == Placement.Bottom) {
221              this.placement = Placement.Top
222            } else {
223              this.placement = undefined
224            }
225          })
226      }
227
228      Row() {
229        Button('Menu')
230          .fontWeight(FontWeight.Bold)
231          .bindMenu(this.MyMenu1(), {
232            enableHoverMode: this.enableHoverMode,
233            showInSubWindow: this.showInSubwindow,
234            placement: this.placement
235          })
236
237        Select([{ value: 'text1' }, { value: 'text2' }, { value: 'text3' }, { value: 'text4' }, { value: 'text5' },
238          { value: 'text6' }, { value: 'text7' }, { value: 'text8' }, { value: 'text9' }, { value: 'text10' }, { value: 'text11' },
239          { value: 'text12' }])
240          .value("Select")
241
242      }
243      .alignRules({
244        center: { anchor: '__container__', align: VerticalAlign.Center },
245        middle: { anchor: '__container__', align: HorizontalAlign.Center }
246      })
247      .margin({
248        top: this.index == 2 ? 330 : this.index == 1 ? 50 : 0,
249        bottom: this.index == 0 ? 330 : 0
250      })
251    }
252    .height('100%')
253    .width('100%')
254  }
255}
256
257```
258
259## 控制子窗菜单的事件透传
260
261当菜单在子窗口中弹出时,默认情况下,菜单周围的事件会传递至所在窗口。从API version 20开始,开发者可通过[ContextMenuOptions](../reference/apis-arkui/arkui-ts/ts-universal-attributes-menu.md#contextmenuoptions10)的modalMode属性设置子菜单弹出时的模态模式,以控制菜单周围事件是否传递。将modalMode设置为ModalMode.TARGET_WINDOW时,菜单周围的事件将不再传递,菜单下方的控件也不会响应事件。
262
263```ts
264@Entry
265@Component
266struct Index2 {
267  build() {
268    Column() {
269    }
270    .bindContextMenu(this.contextMenuBuilder, ResponseType.RightClick, {
271      modalMode: ModalMode.TARGET_WINDOW
272    })
273    .onClick(() => {
274      this.getUIContext().getPromptAction().showToast({
275        message: 'Clicked!'
276      })
277    })
278    .width('100%')
279    .height('100%')
280  }
281
282  @Builder
283  bindMenuBuilder() {
284    Menu() {
285      MenuItem({ content: 'bindMenu item' }) {
286
287      }
288    }
289  }
290
291  @Builder
292  contextMenuBuilder() {
293    Menu() {
294      MenuItem({ content: 'contextMenu item' }) {
295
296      }
297    }
298  }
299}
300
301```
302
303## 基于绑定组件指定位置弹出菜单
304
305菜单从API version 20开始支持基于绑定组件在指定位置弹出。通过设置水平与垂直偏移量,控制菜单相对于绑定组件左上角的弹出位置。与单独使用offset接口不同,此方法可使菜单覆盖显示在绑定组件上。需要指定弹出位置时,可使用[ContextMenuOptions](../reference/apis-arkui/arkui-ts/ts-universal-attributes-menu.md#contextmenuoptions10)的anchorPosition属性进行设置。
306
307> **说明:**
308>- 当菜单处于预览状态时,设定的定位偏移量将无法生效。
309>- 预设的[placement](../reference/apis-arkui/arkui-ts/ts-universal-attributes-menu.md#contextmenuoptions10)对齐参数将不再生效。
310>- 叠加[offset](../reference/apis-arkui/arkui-ts/ts-universal-attributes-menu.md#contextmenuoptions10)参数的偏移量,最终确定菜单的精确显示位置。
311>- 当水平与垂直偏移量均设为负值时,菜单以绑定组件左下角为基准点进行显示。
312>- 当水平或垂直偏移量存在负值时,组件将以绑定组件的左上角为定位基准点,通过叠加偏移量参数实现反向偏移。
313
314```ts
315@Entry
316@Component
317struct DirectiveMenuExample {
318  @Builder
319  MenuBuilder() {
320    Column() {
321      Menu() {
322        MenuItemGroup() {
323          // $r('app.media.app_icon')需要替换为开发者所需的图像资源文件。
324          MenuItem({ startIcon: $r('app.media.app_icon'), content: "Select Mixed Menu 1", labelInfo: "" })
325          MenuItem({ startIcon: $r('app.media.app_icon'), content: "Select Mixed Menu 2", labelInfo: "" })
326          MenuItem({ startIcon: $r('app.media.app_icon'), content: "Select Mixed Menu 3", labelInfo: "" })
327        }
328      }
329    }
330  }
331
332  build() {
333    Column() {
334      Text()
335        .borderRadius(10)
336        .width(200)
337        .height(150)
338        .borderWidth(1)
339        .backgroundColor(Color.White)
340        .borderColor(Color.Red)
341        .margin({ top: 200, left: 125 })
342        .bindContextMenu(this.MenuBuilder, ResponseType.RightClick, {
343          anchorPosition: { x: 45, y: 50 },
344        })
345    }
346    .alignItems(HorizontalAlign.Start)
347    .width('100%')
348    .height('100%')
349    .backgroundColor('#F5F5F5')
350  }
351}
352```
353
354![AnchorPosition](figures/AnchorPosition.gif)
355
356