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  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  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  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 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 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 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