1# 基础自定义弹出框 (CustomDialog)(不推荐) 2CustomDialog是自定义弹出框,可用于广告、中奖、警告、软件更新等与用户交互响应操作。开发者可以通过CustomDialogController类显示自定义弹出框。具体用法请参考[自定义弹出框](../reference/apis-arkui/arkui-ts/ts-methods-custom-dialog-box.md)。 3 4> **说明:** 5> 6> 当前,ArkUI弹出框均为非页面级弹出框,在页面路由跳转时,如果开发者未调用close方法将其关闭,弹出框将不会自动关闭。若需实现在跳转页面时覆盖弹出框的场景,建议使用Navigation。具体使用方法,请参考[组件导航子页面显示类型的弹窗类型](arkts-navigation-navigation.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 .margin({ top: 10, bottom: 10 }) 26 } 27 } 28 } 29 ``` 302. 创建构造器,与装饰器呼应相连。 31 32 ```ts 33 @Entry 34 @Component 35 struct CustomDialogUser { 36 dialogController: CustomDialogController = new CustomDialogController({ 37 builder: CustomDialogExample(), 38 }) 39 } 40 ``` 413. 点击与onClick事件绑定的组件使弹出框弹出。 42 43 ```ts 44 @Entry 45 @Component 46 struct CustomDialogUser { 47 dialogController: CustomDialogController = new CustomDialogController({ 48 builder: CustomDialogExample(), 49 }) 50 51 build() { 52 Column() { 53 Button('click me') 54 .onClick(() => { 55 this.dialogController.open() 56 }) 57 }.width('100%').margin({ top: 5 }) 58 } 59 } 60 ``` 61 62  63 64## 弹出框的交互 65 66弹出框可用于数据交互,完成用户一系列响应操作。 67 681. 在\@CustomDialog装饰器内添加按钮,同时添加数据函数。 69 70 ```ts 71 @CustomDialog 72 struct CustomDialogExample { 73 cancel?: () => void 74 confirm?: () => void 75 controller: CustomDialogController 76 77 build() { 78 Column() { 79 Text('我是内容').fontSize(20).margin({ top: 10, bottom: 10 }) 80 Flex({ justifyContent: FlexAlign.SpaceAround }) { 81 Button('cancel') 82 .onClick(() => { 83 this.controller.close() 84 if (this.cancel) { 85 this.cancel() 86 } 87 }).backgroundColor(0xffffff).fontColor(Color.Black) 88 Button('confirm') 89 .onClick(() => { 90 this.controller.close() 91 if (this.confirm) { 92 this.confirm() 93 } 94 }).backgroundColor(0xffffff).fontColor(Color.Red) 95 }.margin({ bottom: 10 }) 96 } 97 } 98 } 99 ``` 1002. 页面内需要在构造器内进行接收,同时创建相应的函数操作。 101 102 ```ts 103 @Entry 104 @Component 105 struct CustomDialogUser { 106 dialogController: CustomDialogController = new CustomDialogController({ 107 builder: CustomDialogExample({ 108 cancel: ()=> { this.onCancel() }, 109 confirm: ()=> { this.onAccept() }, 110 }), 111 }) 112 113 onCancel() { 114 console.info('Callback when the first button is clicked') 115 } 116 117 onAccept() { 118 console.info('Callback when the second button is clicked') 119 } 120 121 build() { 122 Column() { 123 Button('click me') 124 .onClick(() => { 125 this.dialogController.open() 126 }) 127 }.width('100%').margin({ top: 5 }) 128 } 129 } 130 ``` 131 132  133 134 3.可通过弹出框中的按钮实现路由跳转,同时获取跳转页面向当前页传入的参数。 135 136 ```ts 137 // Index.ets 138 @CustomDialog 139 struct CustomDialogExample { 140 @Link textValue: string 141 controller?: CustomDialogController 142 cancel: () => void = () => { 143 } 144 confirm: () => void = () => { 145 } 146 147 build() { 148 Column({ space: 20 }) { 149 if (this.textValue != '') { 150 Text(`第二个页面的内容为:${this.textValue}`) 151 .fontSize(20) 152 } else { 153 Text('是否获取第二个页面的内容') 154 .fontSize(20) 155 } 156 Flex({ justifyContent: FlexAlign.SpaceAround }) { 157 Button('cancel') 158 .onClick(() => { 159 if (this.controller != undefined) { 160 this.controller.close() 161 this.cancel() 162 } 163 }).backgroundColor(0xffffff).fontColor(Color.Black) 164 Button('confirm') 165 .onClick(() => { 166 if (this.controller != undefined && this.textValue != '') { 167 this.controller.close() 168 } else if (this.controller != undefined) { 169 this.getUIContext().getRouter().pushUrl({ 170 url: 'pages/Index2' 171 }) 172 this.controller.close() 173 } 174 }).backgroundColor(0xffffff).fontColor(Color.Red) 175 }.margin({ bottom: 10 }) 176 }.borderRadius(10).padding({ top: 20 }) 177 } 178 } 179 180 @Entry 181 @Component 182 struct CustomDialogUser { 183 @State textValue: string = '' 184 dialogController: CustomDialogController | null = new CustomDialogController({ 185 builder: CustomDialogExample({ 186 cancel: () => { 187 this.onCancel() 188 }, 189 confirm: () => { 190 this.onAccept() 191 }, 192 textValue: $textValue 193 }) 194 }) 195 196 // 在自定义组件即将析构销毁时将dialogController置空 197 aboutToDisappear() { 198 this.dialogController = null // 将dialogController置空 199 } 200 201 onPageShow() { 202 const params = this.getUIContext().getRouter().getParams() as Record<string, string>; // 获取传递过来的参数对象 203 if (params) { 204 this.dialogController?.open() 205 this.textValue = params.info as string; // 获取info属性的值 206 } 207 } 208 209 onCancel() { 210 console.info('Callback when the first button is clicked') 211 } 212 213 onAccept() { 214 console.info('Callback when the second button is clicked') 215 } 216 217 exitApp() { 218 console.info('Click the callback in the blank area') 219 } 220 221 build() { 222 Column() { 223 Button('click me') 224 .onClick(() => { 225 if (this.dialogController != null) { 226 this.dialogController.open() 227 } 228 }).backgroundColor(0x317aff) 229 }.width('100%').margin({ top: 5 }) 230 } 231 } 232 ``` 233 234 ```ts 235 // Index2.ets 236 @Entry 237 @Component 238 struct Index2 { 239 @State message: string = '点击返回'; 240 build() { 241 Column() { 242 Button(this.message) 243 .fontSize(50) 244 .fontWeight(FontWeight.Bold).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