1# 菜单控制 2 3为组件绑定弹出式菜单,弹出式菜单以垂直列表形式显示菜单项,可通过长按、点击或鼠标右键触发。 4 5> **说明:** 6> 7> - 从API Version 7开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。 8> 9> - CustomBuilder里不支持再使用bindMenu、bindContextMenu弹出菜单。多级菜单可使用[Menu组件](ts-basic-components-menu.md)。 10> 11> - 弹出菜单的文本内容不支持长按选中。 12 13## bindMenu 14 15bindMenu(content: Array<MenuElement> | CustomBuilder, options?: MenuOptions) 16 17给组件绑定菜单,点击后弹出菜单。弹出菜单项支持图标+文本排列和自定义两种功能。 18 19**系统能力:** SystemCapability.ArkUI.ArkUI.Full 20 21**参数:** 22 23| 参数名 | 类型 | 必填 | 说明 | 24| ------- | ------------------------------------------------------------ | ---- | -------------------------------------------- | 25| content | Array<[MenuElement](#menuelement)> \| [CustomBuilder](ts-types.md#custombuilder8) | 是 | 配置菜单项图标和文本的数组,或者自定义组件。 | 26| options | [MenuOptions](#menuoptions10) | 否 | 配置弹出菜单的参数。 | 27 28## bindMenu<sup>11+</sup> 29 30bindMenu(isShow: boolean, content: Array<MenuElement> | CustomBuilder, options?: MenuOptions) 31 32给组件绑定菜单,点击后弹出菜单。弹出菜单项支持图标+文本排列和自定义两种功能。 33 34**系统能力:** SystemCapability.ArkUI.ArkUI.Full 35 36**参数:** 37 38| 参数名 | 类型 | 必填 | 说明 | 39| -------------------- | ------------------------------------------------------------ | ---- | ------------------------------------------------------------ | 40| isShow<sup>11+</sup> | boolean | 是 | 支持开发者通过状态变量控制显隐,默认值为false,menu弹窗必须等待页面全部构建才能展示,因此不能在页面构建中设置为true,否则会导致显示位置及形状错误,当前不支持双向绑定。 | 41| content | Array<[MenuElement](#menuelement)> \| [CustomBuilder](ts-types.md#custombuilder8) | 是 | 配置菜单项图标和文本的数组,或者自定义组件。 | 42| options | [MenuOptions](#menuoptions10) | 否 | 配置弹出菜单的参数。 | 43 44## bindContextMenu<sup>8+</sup> 45 46bindContextMenu(content: CustomBuilder, responseType: ResponseType, options?: ContextMenuOptions) 47 48给组件绑定菜单,触发方式为长按或者右键点击,弹出菜单项需要自定义。 49 50**系统能力:** SystemCapability.ArkUI.ArkUI.Full 51 52**参数:** 53 54| 参数名 | 类型 | 必填 | 说明 | 55| ------------ | -------------------------------------------------- | ---- | -------------------------------------------- | 56| content | [CustomBuilder](ts-types.md#custombuilder8) | 是 | 配置菜单项图标和文本的数组,或者自定义组件。 | 57| responseType | [ResponseType](ts-appendix-enums.md#responsetype8) | 是 | 菜单弹出条件,长按或者右键点击。 | 58| options | [ContextMenuOptions](#contextmenuoptions10) | 否 | 配置弹出菜单的参数。 | 59 60## MenuElement 61 62| 名称 | 类型 | 必填 | 描述 | 63| --------------------- | -------------------------------------- | ---- | ------------------------------------------------------------ | 64| value | [ResourceStr](ts-types.md#resourcestr) | 是 | 菜单项文本。 | 65| icon<sup>10+</sup> | [ResourceStr](ts-types.md#resourcestr) | 否 | 菜单项图标。 | 66| enabled<sup>11+</sup> | boolean | 否 | 菜单条目是否可进行交互。<br/>默认值:true, 菜单条目可以进行交互。 | 67| action | () => void | 是 | 点击菜单项的事件回调。 | 68 69## MenuOptions<sup>10+</sup> 70 71继承自[ContextMenuOptions](#contextmenuoptions10)。 72 73| 名称 | 类型 | 必填 | 描述 | 74| ----------------------------- | -------------------------------------- | ---- | ------------------------------------------------------------ | 75| title | [ResourceStr](ts-types.md#resourcestr) | 否 | 菜单标题。<br>**说明:**<br/>仅在content设置为Array<[MenuElement](#menuelement)> 时生效。 | 76| showInSubWindow<sup>11+</sup> | boolean | 否 | 是否在子窗口显示菜单。<br/>默认值:2in1设备为true,其他设备为false。 | 77 78## ContextMenuOptions<sup>10+</sup> 79 80| 名称 | 类型 | 必填 | 描述 | 81| --------------------- | ------------------------------------------------------------ | ---- | ------------------------------------------------------------ | 82| offset | [Position](ts-types.md#position8) | 否 | 菜单弹出位置的偏移量,不会导致菜单显示超出屏幕范围。<br/>**说明:**<br />菜单类型为相对⽗组件区域弹出时,⾃动根据菜单位置属性 (placement)将区域的宽或⾼计⼊偏移量中。<br/>当菜单相对父组件出现在上侧时(placement设置为Placement.TopLeft,Placement.Top,Placement.TopRight),x为正值,菜单相对组件向右进行偏移,y为正值,菜单相对组件向上进行偏移。<br/>当菜单相对父组件出现在下侧时(placement设置为Placement.BottomLeft,Placement.Bottom,Placement.BottomRight),x为正值,菜单相对组件向右进行偏移,y为正值,菜单相对组件向下进行偏移。<br/>当菜单相对父组件出现在左侧时(placement设置为Placement.LeftTop,Placement.Left,Placement.LeftBottom),x为正值,菜单相对组件向左进行偏移,y为正值,菜单相对组件向下进行偏移。<br/>当菜单相对父组件出现在右侧时(placement设置为Placement.RightTop,Placement.Right,Placement.RightBottom),x为正值,菜单相对组件向右进行偏移,y为正值,菜单相对组件向下进行偏移。<br/>如果菜单调整了显示位置(与placement初始值主方向不⼀致),则偏移值 (offset) 失效。 | 83| placement | [Placement](ts-appendix-enums.md#placement8) | 否 | 菜单组件优先显示的位置,当前位置显示不下时,会自动调整位置。<br/>**说明:**<br />placement值设置为undefined、null或没有设置此选项时,按未设置placement处理,当使用[bindContextMenu<sup>8+</sup>](#bindcontextmenu8),菜单跟随点击位置弹出。 | 84| enableArrow | boolean | 否 | 是否显示箭头。如果菜单的大小和位置不足以放置箭头时,不会显示箭头。 <br/>默认值:false, 不显示箭头。<br/>**说明:**<br />enableArrow为true时,placement未设置或者值为非法值,默认在目标物上方显示,否则按照placement的位置优先显示。当前位置显示不下时,会自动调整位置,enableArrow为undefined时,不显示箭头。 | 85| arrowOffset | [Length](ts-types.md#length) | 否 | 箭头在菜单处的偏移。箭头在菜单水平方向时,偏移量为箭头至最左侧的距离,默认居中。箭头在菜单竖直方向时,偏移量为箭头至最上侧的距离,默认居中。偏移量必须合法且转换为具体数值时大于0才会生效,另外该值生效时不会导致箭头超出菜单四周的安全距离。根据配置的placement来计算是在水平还是竖直方向上偏移。 | 86| preview<sup>11+</sup> | [MenuPreviewMode](ts-appendix-enums.md#menupreviewmode11)\| [CustomBuilder](ts-types.md#custombuilder8) | 否 | 默认值:MenuPreviewMode.NONE, 无预览内容。<br/>**说明:**<br />- 不支持responseType为ResponseType.RightClick时触发,如果responseType为ResponseType.RightClick,则不会显示预览内容。<br />- 当未设置preview参数或preview参数设置为MenuPreviewMode.NONE时,enableArrow参数生效。<br />- 当preview参数设置为MenuPreviewMode.IMAGE或CustomBuilder时,enableArrow为true时也不显示箭头。 | 87| previewAnimationOptions<sup>11+</sup> | [ContextMenuAnimationOptions](#contextmenuanimationoptions11) | 否 | 控制长按预览显示动画开始倍率和结束倍率(相对预览原图比例)。<br/>默认值:{scale: [0.95, 1.1]}。<br/>**说明:**<br />-倍率设置参数小于等于0时,不生效。<br />-当前只在preview设置为MenuPreviewMode.IMAGE模式时生效。 | 88| onAppear | () => void | 否 | 菜单弹出时的事件回调。 | 89| onDisappear | () => void | 否 | 菜单消失时的事件回调。 | 90| aboutToAppear | () => void | 否 | 菜单显示动效前的事件回调。 | 91| aboutToDisappear | () => void | 否 | 菜单退出动效前的事件回调。 | 92| backgroundColor<sup>11+</sup> | [ResourceColor](ts-types.md#resourcecolor) | 否 | 弹窗背板颜色。<br/>默认值:Color.Transparent。 | 93| backgroundBlurStyle<sup>11+</sup> | [BlurStyle](ts-appendix-enums.md#blurstyle9) | 否 | 弹窗背板模糊材质。<br/>默认值:BlurStyle.COMPONENT_ULTRA_THICK。 | 94 95## ContextMenuAnimationOptions<sup>11+</sup> 96 97| 名称 | 类型 | 必填 | 描述 | 98| ----- | ------------------------------------------ | ---- | ------------------------------------ | 99| scale | [AnimationRange](#animationrange11)\<number> | 否 | 动画开始和结束时相对预览原图缩放比例。 | 100 101## AnimationRange<sup>11+</sup> 102 103表示动画开始和结束时相对预览原图缩放比例。 104 105系统能力:SystemCapability.ArkUI.ArkUI.Full 106 107| 取值范围 | 说明 | 108| ---------------- | ------------------------------------------------------------------------------ | 109| [from: T, to: T] | from表示动画开始时相对预览原图缩放比例,to表示动画结束时相对预览原图缩放比例。 | 110 111## 示例 112 113### 示例1 114 115普通菜单 116 117```ts 118// xxx.ets 119@Entry 120@Component 121struct MenuExample { 122 build() { 123 Column() { 124 Text('click for Menu') 125 } 126 .width('100%') 127 .margin({ top: 5 }) 128 .bindMenu([ 129 { 130 value: 'Menu1', 131 action: () => { 132 console.info('handle Menu1 select') 133 } 134 }, 135 { 136 value: 'Menu2', 137 action: () => { 138 console.info('handle Menu2 select') 139 } 140 }, 141 ]) 142 } 143} 144``` 145 146![zh-cn_image_0000001174582862](figures/zh-cn_image_0000001174582862.gif) 147 148### 示例2 149 150自定义内容菜单 151 152```ts 153@Entry 154@Component 155struct MenuExample { 156 @State listData: number[] = [0, 0, 0] 157 158 @Builder MenuBuilder() { 159 Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { 160 ForEach(this.listData, (item:number, index) => { 161 Column() { 162 Row() { 163 Image($r("app.media.icon")).width(20).height(20).margin({ right: 5 }) 164 Text(`Menu${index as number + 1}`).fontSize(20) 165 } 166 .width('100%') 167 .height(30) 168 .justifyContent(FlexAlign.Center) 169 .align(Alignment.Center) 170 .onClick(() => { 171 console.info(`Menu${index as number + 1} Clicked!`) 172 }) 173 174 if (index != this.listData.length - 1) { 175 Divider().height(10).width('80%').color('#ccc') 176 } 177 }.padding(5).height(40) 178 }) 179 }.width(100) 180 } 181 182 build() { 183 Column() { 184 Text('click for menu') 185 .fontSize(20) 186 .margin({ top: 20 }) 187 .bindMenu(this.MenuBuilder) 188 } 189 .height('100%') 190 .width('100%') 191 .backgroundColor('#f0f0f0') 192 } 193} 194``` 195 196![zh-cn_image_0000001186807708](figures/zh-cn_image_0000001186807708.gif) 197 198### 示例3 199 200菜单(长按触发显示) 201 202```ts 203// xxx.ets 204@Entry 205@Component 206struct ContextMenuExample { 207 @Builder MenuBuilder() { 208 Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { 209 Text('Test menu item 1') 210 .fontSize(20) 211 .width(100) 212 .height(50) 213 .textAlign(TextAlign.Center) 214 Divider().height(10) 215 Text('Test menu item 2') 216 .fontSize(20) 217 .width(100) 218 .height(50) 219 .textAlign(TextAlign.Center) 220 }.width(100) 221 } 222 223 build() { 224 Column() { 225 Text('LongPress for menu') 226 } 227 .width('100%') 228 .margin({ top: 5 }) 229 .bindContextMenu(this.MenuBuilder, ResponseType.LongPress) 230 } 231} 232``` 233 234![longMenu](figures/longMenu.gif) 235 236### 示例4 237 238指向性菜单(右键触发显示) 239 240```ts 241// xxx.ets 242@Entry 243@Component 244struct DirectiveMenuExample { 245 @Builder MenuBuilder() { 246 Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { 247 Text('Options') 248 Divider().strokeWidth(2).margin(5).color('#F0F0F0') 249 Text('Hide') 250 Divider().strokeWidth(2).margin(5).color('#F0F0F0') 251 Text('Exit') 252 } 253 .width(200) 254 } 255 256 build() { 257 Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { 258 Column() { 259 Text("DirectiveMenuExample") 260 .fontSize(20) 261 .width('100%') 262 .height("25%") 263 .backgroundColor('#F0F0F0') 264 .textAlign(TextAlign.Center) 265 .bindContextMenu(this.MenuBuilder, ResponseType.RightClick, { 266 enableArrow: true, 267 placement: Placement.Bottom 268 }) 269 } 270 } 271 .width('100%') 272 .height('100%') 273 } 274} 275``` 276 277![zh-cn_image_0000001689126950](figures/zh-cn_image_0000001689126950.png) 278 279### 示例5 280 281长按悬浮菜单(预览内容为截图形式) 282 283```ts 284// xxx.ets 285@Entry 286@Component 287struct Index { 288 private iconStr: ResourceStr = $r("app.media.icon") 289 290 @Builder 291 MyMenu() { 292 Menu() { 293 MenuItem({ startIcon: this.iconStr, content: "菜单选项" }) 294 MenuItem({ startIcon: this.iconStr, content: "菜单选项" }) 295 MenuItem({ startIcon: this.iconStr, content: "菜单选项" }) 296 } 297 } 298 299 build() { 300 Column({ space: 50 }) { 301 Column() { 302 Column() { 303 Text('preview-image') 304 .width(200) 305 .height(100) 306 .textAlign(TextAlign.Center) 307 .margin(100) 308 .fontSize(30) 309 .bindContextMenu(this.MyMenu, ResponseType.LongPress, 310 { preview: MenuPreviewMode.IMAGE, 311 previewAnimationOptions: {scale: [0.8, 1.0]}, 312 }) 313 .backgroundColor("#ff3df2f5") 314 } 315 }.width('100%') 316 } 317 } 318} 319``` 320 321![preview-image](figures/preview-image.png) 322 323### 示例6 324 325长按悬浮菜单(自定义预览内容) 326 327```ts 328// xxx.ets 329@Entry 330@Component 331struct Index { 332 private iconStr: ResourceStr = $r("app.media.icon") 333 334 @Builder 335 MyMenu() { 336 Menu() { 337 MenuItem({ startIcon: this.iconStr, content: "菜单选项" }) 338 MenuItem({ startIcon: this.iconStr, content: "菜单选项" }) 339 MenuItem({ startIcon: this.iconStr, content: "菜单选项" }) 340 } 341 } 342 343 @Builder 344 MyPreview() { 345 Column() { 346 Image($r('app.media.icon')) 347 .width(200) 348 .height(200) 349 } 350 } 351 352 build() { 353 Column({ space: 50 }) { 354 Column() { 355 Column() { 356 Text('preview-builder') 357 .width(200) 358 .height(100) 359 .textAlign(TextAlign.Center) 360 .margin(100) 361 .fontSize(30) 362 .bindContextMenu(this.MyMenu, ResponseType.LongPress, 363 { 364 preview: this.MyPreview 365 }) 366 } 367 }.width('100%') 368 } 369 } 370} 371``` 372 373![preview-builder](figures/preview-builder.png) 374 375### 示例7 376 377通过绑定isShown控制菜单(自定义预览内容) 378 379```ts 380// xxx.ets 381@Entry 382@Component 383struct Index { 384 private iconStr: ResourceStr = $r("app.media.icon") 385 @State isShown: boolean = false 386 387 @Builder 388 MyMenu() { 389 Menu() { 390 MenuItem({ startIcon: this.iconStr, content: "菜单选项" }) 391 MenuItem({ startIcon: this.iconStr, content: "菜单选项" }) 392 MenuItem({ startIcon: this.iconStr, content: "菜单选项" }) 393 } 394 } 395 396 @Builder 397 MyPreview() { 398 Column() { 399 Image($r('app.media.icon')) 400 .width(200) 401 .height(200) 402 } 403 } 404 405 build() { 406 Column({ space: 50 }) { 407 Column() { 408 Column() { 409 Text('preview-builder') 410 .width(200) 411 .height(100) 412 .textAlign(TextAlign.Center) 413 .margin(100) 414 .fontSize(30) 415 .bindContextMenu(this.isShown, this.MyMenu, 416 { 417 preview: this.MyPreview, 418 onDisappear: ()=>{ 419 this.isShown = false; 420 } 421 }) 422 Button('click') 423 .onClick(()=>{ 424 this.isShown = true; 425 }) 426 } 427 }.width('100%') 428 } 429 } 430} 431``` 432