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