• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 基础自定义弹出框 (CustomDialog)(不推荐)
2CustomDialog是自定义弹出框,可用于广告、中奖、警告、软件更新等与用户交互响应操作。开发者可以通过CustomDialogController类显示自定义弹出框。具体用法请参考[自定义弹出框](../reference/apis-arkui/arkui-ts/ts-methods-custom-dialog-box.md)。
3
4> **说明:**
5>
6> 当前,ArkUI弹出框默认为非页面级弹出框,在页面路由跳转时,如果开发者未调用close方法将其关闭,弹出框将不会自动关闭。若需实现在跳转页面时覆盖弹出框的场景,可以使用[组件导航子页面显示类型的弹窗类型](arkts-navigation-navigation.md#页面显示类型)或者[页面级弹出框](arkts-embedded-dialog.md)。
7
8弹出框(CustomDialog)可以通过配置[isModal](../reference/apis-arkui/arkui-ts/ts-methods-custom-dialog-box.md#customdialogcontrolleroptions对象说明)来实现模态和非模态弹窗。isModal为true的时候,弹出框为模态弹窗。isModal为false时,弹出框为非模态弹窗。
9
10## 创建自定义弹出框
11
121. 使用\@CustomDialog装饰器装饰自定义弹出框,可在此装饰器内自定义弹出框内容。CustomDialogController需在@Component内定义。
13
14   ```ts
15   @CustomDialog
16   struct CustomDialogExample {
17     controller: CustomDialogController = new CustomDialogController({
18       builder: CustomDialogExample({}),
19     })
20
21     build() {
22       Column() {
23         Text('我是内容')
24           .fontSize(20)
25       }.height(60).justifyContent(FlexAlign.Center)
26     }
27   }
28   ```
292. 创建构造器,与装饰器呼应相连。
30
31   ```ts
32    @Entry
33    @Component
34    struct CustomDialogUser {
35      dialogController: CustomDialogController = new CustomDialogController({
36        builder: CustomDialogExample(),
37      })
38    }
39   ```
403. 点击与onClick事件绑定的组件使弹出框弹出。
41
42   ```ts
43   @Entry
44   @Component
45   struct CustomDialogUser {
46     dialogController: CustomDialogController = new CustomDialogController({
47       builder: CustomDialogExample(),
48     })
49
50     build() {
51       Column() {
52         Button('click me')
53           .onClick(() => {
54             this.dialogController.open()
55           })
56       }.width('100%').margin({ top: 5 })
57     }
58   }
59   ```
60
61   ![zh-cn_image_0000001562700493](figures/zh-cn_image_0000001562700493.png)
62
63## 弹出框的交互
64
65弹出框可用于数据交互,完成用户一系列响应操作。
66
671. 在\@CustomDialog装饰器内添加按钮,同时添加数据函数。
68
69   ```ts
70   @CustomDialog
71   struct CustomDialogExample {
72     cancel: () => void = () => {
73     }
74     confirm: () => void = () => {
75     }
76     controller: CustomDialogController
77
78     build() {
79       Column() {
80         Text('我是内容').fontSize(20).margin({ top: 10, bottom: 10 })
81         Flex({ justifyContent: FlexAlign.SpaceAround }) {
82           Button('cancel')
83             .onClick(() => {
84               this.controller.close()
85               if (this.cancel) {
86                 this.cancel()
87               }
88             }).backgroundColor(0xffffff).fontColor(Color.Black)
89           Button('confirm')
90             .onClick(() => {
91               this.controller.close()
92               if (this.confirm) {
93                 this.confirm()
94               }
95             }).backgroundColor(0xffffff).fontColor(Color.Red)
96         }.margin({ bottom: 10 })
97       }
98     }
99   }
100   ```
1012. 页面内需要在构造器内进行接收,同时创建相应的函数操作。
102
103   ```ts
104   @Entry
105   @Component
106   struct CustomDialogUser {
107     dialogController: CustomDialogController = new CustomDialogController({
108       builder: CustomDialogExample({
109         cancel: ()=> { this.onCancel() },
110         confirm: ()=> { this.onAccept() },
111       }),
112     })
113
114     onCancel() {
115       console.info('Callback when the first button is clicked')
116     }
117
118     onAccept() {
119       console.info('Callback when the second button is clicked')
120     }
121
122     build() {
123       Column() {
124         Button('click me')
125           .onClick(() => {
126             this.dialogController.open()
127           })
128       }.width('100%').margin({ top: 5 })
129     }
130   }
131   ```
132
133   ![zh-cn_image_0000001511421320](figures/zh-cn_image_0000001511421320.png)
134
135   3.可通过弹出框中的按钮实现路由跳转,同时获取跳转页面向当前页传入的参数。
136
137   ```ts
138   // Index.ets
139   @CustomDialog
140   struct CustomDialogExample {
141     @Link textValue: string
142     controller?: CustomDialogController
143     cancel: () => void = () => {
144     }
145     confirm: () => void = () => {
146     }
147
148     build() {
149       Column({ space: 20 }) {
150         if (this.textValue != '') {
151           Text(`第二个页面的内容为:${this.textValue}`)
152             .fontSize(20)
153         } else {
154           Text('是否获取第二个页面的内容')
155             .fontSize(20)
156         }
157         Flex({ justifyContent: FlexAlign.SpaceAround }) {
158           Button('cancel')
159             .onClick(() => {
160               if (this.controller != undefined) {
161                 this.controller.close()
162                 this.cancel()
163               }
164             }).backgroundColor(0xffffff).fontColor(Color.Black)
165           Button('confirm')
166             .onClick(() => {
167               if (this.controller != undefined && this.textValue != '') {
168                 this.controller.close()
169               } else if (this.controller != undefined) {
170                 this.getUIContext().getRouter().pushUrl({
171                   url: 'pages/Index2'
172                 })
173                 this.controller.close()
174               }
175             }).backgroundColor(0xffffff).fontColor(Color.Red)
176         }.margin({ bottom: 10 })
177       }.borderRadius(10).padding({ top: 20 })
178     }
179   }
180
181   @Entry
182   @Component
183   struct CustomDialogUser {
184     @State textValue: string = ''
185     dialogController: CustomDialogController | null = new CustomDialogController({
186       builder: CustomDialogExample({
187         cancel: () => {
188           this.onCancel()
189         },
190         confirm: () => {
191           this.onAccept()
192         },
193         textValue: this.textValue
194       })
195     })
196
197     // 在自定义组件即将析构销毁时将dialogController置空
198     aboutToDisappear() {
199       this.dialogController = null // 将dialogController置空
200     }
201
202     onPageShow() {
203       const params = this.getUIContext().getRouter().getParams() as Record<string, string>; // 获取传递过来的参数对象
204       if (params) {
205         this.dialogController?.open()
206         this.textValue = params.info as string; // 获取info属性的值
207       }
208     }
209
210     onCancel() {
211       console.info('Callback when the first button is clicked')
212     }
213
214     onAccept() {
215       console.info('Callback when the second button is clicked')
216     }
217
218     exitApp() {
219       console.info('Click the callback in the blank area')
220     }
221
222     build() {
223       Column() {
224         Button('click me')
225           .onClick(() => {
226             if (this.dialogController != null) {
227               this.dialogController.open()
228             }
229           }).backgroundColor(0x317aff)
230       }.width('100%').margin({ top: 5 })
231     }
232   }
233   ```
234
235   ```ts
236   // Index2.ets
237   @Entry
238   @Component
239   struct Index2 {
240     @State message: string = '点击返回';
241
242     build() {
243       Column() {
244         Button(this.message)
245           .type(ButtonType.Capsule)
246           .onClick(() => {
247              this.getUIContext().getRouter().back({
248                url: 'pages/Index',
249                params: {
250                info: 'Hello World'
251              }
252           });
253         })
254       }.width('100%').height('100%').margin({ top: 20 })
255     }
256   }
257   ```
258
259   ![DialogRouter](figures/DialogRouter.gif)
260
261## 弹出框的动画
262
263弹出框通过定义openAnimation控制弹出框出现动画的持续时间,速度等参数。
264
265```ts
266@CustomDialog
267struct CustomDialogExample {
268  controller?: CustomDialogController
269
270  build() {
271    Column() {
272      Text('Whether to change a text?').fontSize(16).margin({ bottom: 10 })
273    }
274  }
275}
276
277@Entry
278@Component
279struct CustomDialogUser {
280  @State textValue: string = ''
281  @State inputValue: string = 'click me'
282  dialogController: CustomDialogController | null = new CustomDialogController({
283    builder: CustomDialogExample(),
284    openAnimation: {
285      duration: 1200,
286      curve: Curve.Friction,
287      delay: 500,
288      playMode: PlayMode.Alternate,
289      onFinish: () => {
290        console.info('play end')
291      }
292    },
293    autoCancel: true,
294    alignment: DialogAlignment.Bottom,
295    offset: { dx: 0, dy: -20 },
296    gridCount: 4,
297    customStyle: false,
298    backgroundColor: 0xd9ffffff,
299    cornerRadius: 10,
300  })
301
302  // 在自定义组件即将析构销毁时将dialogController置空
303  aboutToDisappear() {
304    this.dialogController = null // 将dialogController置空
305  }
306
307  build() {
308    Column() {
309      Button(this.inputValue)
310        .onClick(() => {
311          if (this.dialogController != null) {
312            this.dialogController.open()
313          }
314        }).backgroundColor(0x317aff)
315    }.width('100%').margin({ top: 5 })
316  }
317}
318```
319
320![openAnimator](figures/openAnimator.gif)
321
322## 弹出框的样式
323
324弹出框通过定义宽度、高度、背景色、阴影等参数来控制样式。
325
326```ts
327@CustomDialog
328struct CustomDialogExample {
329  controller?: CustomDialogController
330
331  build() {
332    Column() {
333      Text('我是内容').fontSize(16).margin({ bottom: 10 })
334    }
335  }
336}
337
338@Entry
339@Component
340struct CustomDialogUser {
341  @State textValue: string = ''
342  @State inputValue: string = 'click me'
343  dialogController: CustomDialogController | null = new CustomDialogController({
344    builder: CustomDialogExample(),
345    autoCancel: true,
346    alignment: DialogAlignment.Center,
347    offset: { dx: 0, dy: -20 },
348    gridCount: 4,
349    customStyle: false,
350    backgroundColor: 0xd9ffffff,
351    cornerRadius: 20,
352    width: '80%',
353    height: '100px',
354    borderWidth: 1,
355    borderStyle: BorderStyle.Dashed,//使用borderStyle属性,需要和borderWidth属性一起使用
356    borderColor: Color.Blue,//使用borderColor属性,需要和borderWidth属性一起使用
357    shadow: ({ radius: 20, color: Color.Grey, offsetX: 50, offsetY: 0}),
358  })
359
360  // 在自定义组件即将析构销毁时将dialogController置空
361  aboutToDisappear() {
362    this.dialogController = null // 将dialogController置空
363  }
364
365  build() {
366    Column() {
367      Button(this.inputValue)
368        .onClick(() => {
369          if (this.dialogController != null) {
370            this.dialogController.open()
371          }
372        }).backgroundColor(0x317aff)
373    }.width('100%').margin({ top: 5 })
374  }
375}
376```
377
378![custom_style](figures/custom_style.gif)
379
380## 嵌套自定义弹出框
381
382通过第一个弹出框打开第二个弹出框时,最好将第二个弹出框定义在第一个弹出框的父组件处,通过父组件传给第一个弹出框的回调来打开第二个弹出框。
383
384```ts
385@CustomDialog
386struct CustomDialogExampleTwo {
387  controllerTwo?: CustomDialogController
388  @State message: string = "I'm the second dialog box."
389  @State showIf: boolean = false;
390  build() {
391    Column() {
392      if (this.showIf) {
393        Text("Text")
394          .fontSize(30)
395          .height(100)
396      }
397      Text(this.message)
398        .fontSize(30)
399        .height(100)
400      Button("Create Text")
401        .onClick(()=>{
402          this.showIf = true;
403        })
404      Button ('Close Second Dialog Box')
405        .onClick(() => {
406          if (this.controllerTwo != undefined) {
407            this.controllerTwo.close()
408          }
409        })
410        .margin(20)
411    }
412  }
413}
414@CustomDialog
415struct CustomDialogExample {
416  openSecondBox?: ()=>void
417  controller?: CustomDialogController
418
419  build() {
420    Column() {
421      Button ('Open Second Dialog Box and close this box')
422        .onClick(() => {
423          this.controller!.close();
424          this.openSecondBox!();
425        })
426        .margin(20)
427    }.borderRadius(10)
428  }
429}
430@Entry
431@Component
432struct CustomDialogUser {
433  @State inputValue: string = 'Click Me'
434  dialogController: CustomDialogController | null = new CustomDialogController({
435    builder: CustomDialogExample({
436      openSecondBox: ()=>{
437        if (this.dialogControllerTwo != null) {
438          this.dialogControllerTwo.open()
439        }
440      }
441    }),
442    cancel: this.exitApp,
443    autoCancel: true,
444    alignment: DialogAlignment.Bottom,
445    offset: { dx: 0, dy: -20 },
446    gridCount: 4,
447    customStyle: false
448  })
449  dialogControllerTwo: CustomDialogController | null = new CustomDialogController({
450    builder: CustomDialogExampleTwo(),
451    alignment: DialogAlignment.Bottom,
452    offset: { dx: 0, dy: -25 } })
453
454  aboutToDisappear() {
455    this.dialogController = null
456    this.dialogControllerTwo = null
457  }
458
459  onCancel() {
460    console.info('Callback when the first button is clicked')
461  }
462
463  onAccept() {
464    console.info('Callback when the second button is clicked')
465  }
466
467  exitApp() {
468    console.info('Click the callback in the blank area')
469  }
470  build() {
471    Column() {
472      Button(this.inputValue)
473        .onClick(() => {
474          if (this.dialogController != null) {
475            this.dialogController.open()
476          }
477        }).backgroundColor(0x317aff)
478    }.width('100%').margin({ top: 5 })
479  }
480}
481```
482
483![nested_dialog](figures/nested_dialog.gif)
484
485由于自定义弹出框在状态管理侧有父子关系,如果将第二个弹出框定义在第一个弹出框内,那么当父组件(第一个弹出框)被销毁(关闭)时,子组件(第二个弹出框)内无法再继续创建新的组件。
486
487## 相关实例
488
489针对自定义弹出框开发,有以下相关实例可供参考:
490
491- [自定义弹出框(ArkTS)(API9)](https://gitee.com/openharmony/codelabs/tree/master/ETSUI/CustomDialog)
492- [构建多种样式弹出框(ArkTS)(API9)](https://gitee.com/openharmony/codelabs/tree/master/ETSUI/MultipleDialog)
493- [目标管理(ArkTS)(API9)](https://gitee.com/openharmony/codelabs/tree/master/ETSUI/TargetManagement)
494
495
496