1# 弹窗的使用 2 3## 场景说明 4应用中经常用到弹窗,比如警告弹窗、日期选择弹窗、文本选择弹窗以及其他自定义弹窗等等。本例将为大家介绍如何使用不同的弹窗。 5 6## 效果呈现 7本例最终效果如下: 8 9 10 11示例中共涉及四类弹窗: 12- 警告弹窗:提示信息尚未保存。 13- 日期滑动选择器弹窗:选择出生日期。 14- 文本滑动选择器弹窗:选择性别。 15- 自定义弹窗:填写兴趣爱好。 16 17>  **说明:** 18> 自定义弹窗可以根据业务需要自行定义弹窗的形式和内容,比如文本输入、单选、多选等等,本例以文本输入为例进行介绍。 19 20## 运行环境 21本例基于以下环境开发,开发者也可以基于其他适配的版本进行开发: 22 23- IDE: DevEco Studio 3.1 Release 24- SDK: Ohos_sdk_public 3.2.12.5(API Version 9 Release) 25 26 27## 实现思路 28本例中涉及的4类弹窗及实现方案如下: 29- 警告弹窗:使用AlertDialog实现。 30- 日期滑动选择器弹窗:使用DatePickerDialog实现。 31- 文本滑动选择器弹窗:使用TextPickerDialog实现。 32- 自定义弹窗:使用CustomDialogController实现。 33 34## 开发步骤 35由于本例重点讲解对话框的使用,所以开发步骤会着重讲解相关实现,不相关的内容不做介绍,全量代码可参考完整代码章节。 361. 首先,使用AlertDialog实现警告弹窗。 37 38 通过message参数设置告警信息,alignment设置弹窗在界面中垂直方向的对齐方式;通过primaryButton和secondaryButton添加按钮。 39 具体代码如下: 40 41 ```ts 42 alertDialog(context: Context.UIAbilityContext) { 43 AlertDialog.show({ 44 // 通过message设置告警信息 45 message: '当前数据未保存,是否确认离开?', 46 // 通过alignment设置弹窗在界面垂直方向的对齐方式,此处设置为底部对齐 47 alignment: DialogAlignment.Bottom, 48 // 通过offset设置基于对齐位置的便宜量 49 offset: { 50 dx: 0, 51 dy: -20 52 }, 53 // 弹窗中左起第一个按钮 54 primaryButton: { 55 value: '取消', 56 action: () => { 57 console.info('Callback cancel button is clicked'); 58 } 59 }, 60 // 弹窗中左起第二个按钮 61 secondaryButton: { 62 value: '确定', 63 action: () => { 64 // Exiting the app. 65 context.terminateSelf(); 66 console.info('Callback definite button is clicked'); 67 } 68 } 69 }); 70 } 71 ``` 722. 使用DatePickerDialog实现日期滑动选择器弹窗。 73 74 通过start和end分别设置日期区间的起始时间和末尾时间;通过lunar设置使用农历还是阳历;使用onAccept监听选择的日期,本例中通过变量selectedDate将选中的日期设置给参数selected,这样弹窗弹出时的日期就默认为上次选中的日期。 75 具体代码如下: 76 77 ```ts 78 datePickerDialog(dateCallback) { 79 DatePickerDialog.show({ 80 start: new Date('1900-1-1'), 81 end: new Date('2100-1-1'), 82 // 通过变量selectedDate将选中的日期设置给参数selected 83 selected: this.selectedDate, 84 lunar: false, 85 // 使用onAccept监听选择的日期 86 onAccept: (value: DatePickerResult) => { 87 let year = value.year; 88 let month = value.month + 1; 89 let day = value.day; 90 let birthdate: string = this.getBirthDateValue(year, month, day); 91 // 通过setFullYear将选中的日期传递给变量selectedDate 92 this.selectedDate.setFullYear(value.year, value.month, value.day) 93 // 返回选中的日期 94 dateCallback(birthdate); 95 } 96 }); 97 } 98 ``` 993. 使用TextPickerDialog实现文本滑动选择器弹窗。 100 101 通过range设置文本选择项,使用onAccept监听选择的文本项,本例中通过变量selectedGender将选中的性别的索引设置给参数selected,这样弹窗弹出时的性别就默认为上次选中的性别。 102 具体代码如下: 103 104 ```ts 105 textPickerDialog(sexArray: Resource, sexCallback) { 106 // 判断文本项的列表是否为空 107 if (this.isEmptyArr(sexArray)) { 108 console.error('sex is null'); 109 return; 110 } 111 TextPickerDialog.show({ 112 // 通过range设置文本选择项 113 range: sexArray, 114 // 通过变量selectedGender将选中的性别的索引设置给参数selected 115 selected: this.selectedGender, 116 // 使用onAccept监听选择的文本项 117 onAccept: (result: TextPickerResult) => { 118 sexCallback(result.value); 119 // 获取选中项的索引 120 this.selectedGender = result.index 121 }, 122 onCancel: () => { 123 console.info('TextPickerDialog onCancel'); 124 } 125 }); 126 } 127 ``` 1284. 使用CustomDialogController实现自定义弹窗。 129 130 当现有弹窗不能满足业务诉求时,开发者可以自行设计弹窗的样式。在实现自定义弹窗时,需要将弹窗的UI放在被@CustomDialog修饰的自定义组件中,然后使用CustomDialogController的实例来控制弹窗的弹出和关闭。 131 具体代码如下: 132 ```ts 133 // 使用@CustomDialog修饰自定义弹窗 134 @CustomDialog 135 struct CustomDialogFrame{ 136 ... 137 // 定义CustomDialogController 138 controller: CustomDialogController 139 140 build(){ 141 Column() { 142 Text('兴趣爱好').fontSize(20).margin({ top: 10, bottom: 10 }) 143 TextInput({ placeholder: '', text: this.textValue }).height(60).width('90%') 144 .onChange((value: string) => { 145 this.textValue = value 146 }) 147 Flex({ justifyContent: FlexAlign.SpaceAround }) { 148 Button('取消') 149 .onClick(() => { 150 // 点击‘取消’,弹窗关闭 151 this.controller.close() 152 }) 153 .backgroundColor('') 154 .fontColor('#007DFF') 155 Button('保存') 156 .onClick(() => { 157 this.inputValue = this.textValue 158 // 点击‘保存’,弹窗关闭 159 this.controller.close() 160 }) 161 .backgroundColor(0xffffff) 162 .fontColor('#007DFF') 163 }.margin({ bottom: 10 }) 164 }.justifyContent(FlexAlign.Start) 165 } 166 } 167 ... 168 // 实例化自定义弹窗 169 customDialogController: CustomDialogController = new CustomDialogController({ 170 // 使用上文创建的自定义弹窗进行实例化 171 builder: CustomDialogFrame({ 172 textValue: $textValue, 173 inputValue: $inputValue 174 }), 175 alignment: DialogAlignment.Bottom, 176 offset: { 177 dx: 0, 178 dy: -20 179 } 180 }); 181 ... 182 ``` 183## 完整代码 184本例完整代码如下: 185```ts 186import Context from '@ohos.app.ability.common'; 187import hilog from '@ohos.hilog'; 188 189@Component 190struct TextFrame{ 191 @Link content: string; 192 private textImage:Resource; 193 private text:string; 194 onTextClick:()=>void; 195 196 build(){ 197 Row(){ 198 Image(this.textImage) 199 .width(24) 200 .height(24) 201 .margin({left:12}) 202 Text(this.text) 203 .fontSize(16) 204 .margin({ left:12 }) 205 .height(24) 206 Text(this.content) 207 .fontSize(16) 208 .textAlign(TextAlign.End) 209 .textOverflow({ overflow: TextOverflow.Ellipsis }) 210 .maxLines(1) 211 .margin({ 212 left: 16, 213 right: 7 214 }) 215 .layoutWeight(1) 216 .width('100%') 217 Image($r('app.media.ic_arrow')) 218 .width(12) 219 .height(24) 220 .margin({ right: 14 }) 221 } 222 .margin({ top: 24 }) 223 .borderRadius(24) 224 .backgroundColor(Color.White) 225 .width('93.3%') 226 .height(64) 227 .onClick(this.onTextClick) 228 } 229} 230 231@Component 232struct InputFrame{ 233 private inputImage: Resource; 234 private hintText: string; 235 236 build(){ 237 Row() { 238 Image(this.inputImage) 239 .width(24) 240 .height(24) 241 .margin({ left: 12 }) 242 TextInput({ placeholder: this.hintText }) 243 .fontSize(16) 244 .padding({ left: 12 }) 245 .placeholderColor('#99000000') 246 .backgroundColor(Color.White) 247 .fontWeight(FontWeight.Normal) 248 .fontStyle(FontStyle.Normal) 249 .fontColor(Color.Black) 250 .margin({ right: 32 }) 251 .layoutWeight(1) 252 .height(48) 253 } 254 .margin({ top: 24 }) 255 .borderRadius(24) 256 .backgroundColor(Color.White) 257 .width('93.3%') 258 .height(64) 259 } 260} 261 262@CustomDialog 263struct CustomDialogFrame{ 264 @Link textValue: string 265 @Link inputValue: string 266 controller: CustomDialogController 267 268 build(){ 269 Column() { 270 Text('兴趣爱好').fontSize(20).margin({ top: 10, bottom: 10 }) 271 TextInput({ placeholder: '', text: this.textValue }).height(60).width('90%') 272 .onChange((value: string) => { 273 this.textValue = value 274 }) 275 Flex({ justifyContent: FlexAlign.SpaceAround }) { 276 Button('取消') 277 .onClick(() => { 278 this.controller.close() 279 }).backgroundColor('').fontColor('#007DFF') 280 Button('保存') 281 .onClick(() => { 282 this.inputValue = this.textValue 283 this.controller.close() 284 }).backgroundColor(0xffffff).fontColor('#007DFF') 285 }.margin({ bottom: 10 }) 286 }.justifyContent(FlexAlign.Start) 287 } 288} 289 290@Entry 291@Component 292struct Index { 293 @State birthdate: string = ''; 294 @State sex: string = ''; 295 @State textValue: string = ''; 296 @State inputValue: string = ''; 297 selectedDate: Date = new Date("2010-1-1") 298 selectedGender:number = 0 299 private sexArray: Resource = $r('app.strarray.sex_array'); 300 customDialogController: CustomDialogController = new CustomDialogController({ 301 builder: CustomDialogFrame({ 302 textValue: $textValue, 303 inputValue: $inputValue 304 }), 305 alignment: DialogAlignment.Bottom, 306 offset: { 307 dx: 0, 308 dy: -20 309 } 310 }); 311 312 alertDialog(context: Context.UIAbilityContext) { 313 AlertDialog.show({ 314 message: '当前数据未保存,是否确认离开?', 315 alignment: DialogAlignment.Bottom, 316 offset: { 317 dx: 0, 318 dy: -20 319 }, 320 primaryButton: { 321 value: '取消', 322 action: () => { 323 console.info('Callback cancel button is clicked'); 324 } 325 }, 326 secondaryButton: { 327 value: '确定', 328 action: () => { 329 // Exiting the app. 330 context.terminateSelf(); 331 console.info('Callback definite button is clicked'); 332 } 333 } 334 }); 335 } 336 337 datePickerDialog(dateCallback) { 338 DatePickerDialog.show({ 339 start: new Date('1900-1-1'), 340 end: new Date('2100-1-1'), 341 selected: this.selectedDate, 342 lunar: false, 343 onAccept: (value: DatePickerResult) => { 344 let year = value.year; 345 let month = value.month + 1; 346 let day = value.day; 347 let birthdate: string = this.getBirthDateValue(year, month, day); 348 this.selectedDate.setFullYear(value.year, value.month, value.day) 349 dateCallback(birthdate); 350 } 351 }); 352 } 353 354 textPickerDialog(sexArray: Resource, sexCallback) { 355 if (this.isEmptyArr(sexArray)) { 356 console.error('sex is null'); 357 return; 358 } 359 TextPickerDialog.show({ 360 range: sexArray, 361 selected: this.selectedGender, 362 onAccept: (result: TextPickerResult) => { 363 sexCallback(result.value); 364 this.selectedGender = result.index 365 }, 366 onCancel: () => { 367 console.info('TextPickerDialog onCancel'); 368 } 369 }); 370 } 371 372 getBirthDateValue(year: number, month: number, day: number): string { 373 let birthdate: string = `${year}${'年'}${month}` + 374 `${'月'}${day}${'日'}`; 375 return birthdate; 376 } 377 378 isEmpty(obj): boolean { 379 return obj === undefined || obj === null || obj === ''; 380 } 381 382 isEmptyArr(array): boolean { 383 return this.isEmpty(array) || array.length === 0; 384 } 385 386 build() { 387 Row() { 388 Column() { 389 Row(){ 390 Image($r('app.media.ic_back')) 391 .width(26) 392 .height(26) 393 .alignSelf(ItemAlign.Start) 394 .margin({ 395 left: '7.2%', 396 top: 19 397 }) 398 .onClick(() => { 399 let context = getContext(this) as Context.UIAbilityContext; 400 this.alertDialog(context); 401 }) 402 Text('个人信息') 403 .fontColor(Color.Black) 404 .fontSize(20) 405 .margin({ top: 20,left:20 }) 406 .alignSelf(ItemAlign.Center) 407 }.width('100%') 408 Image($r('app.media.ic_avatar')) 409 .width(56) 410 .height(56) 411 .alignSelf(ItemAlign.Center) 412 .margin({ top: '5.5%' }) 413 Text('头像') 414 .fontColor(Color.Black) 415 .fontSize(16) 416 .margin({ top: '2.1%' }) 417 .alignSelf(ItemAlign.Center) 418 InputFrame({ 419 inputImage: $r('app.media.ic_nickname'), 420 hintText: '昵称' 421 }) 422 TextFrame({ 423 textImage: $r('app.media.ic_birthdate'), 424 text: '出生日期', 425 content: $birthdate, 426 onTextClick: () => { 427 this.datePickerDialog((birthValue: string) => { 428 this.birthdate = birthValue; 429 }); 430 } 431 }) 432 TextFrame({ 433 textImage: $r('app.media.ic_sex'), 434 text: '性别', 435 content: $sex, 436 onTextClick: () => { 437 this.textPickerDialog(this.sexArray, (sexValue: string) => { 438 this.sex = sexValue; 439 }); 440 } 441 }) 442 InputFrame({ 443 inputImage: $r('app.media.ic_signature'), 444 hintText: '个性签名' 445 }) 446 TextFrame({ 447 textImage: $r('app.media.ic_hobbies'), 448 text: '兴趣爱好', 449 content: $textValue, 450 onTextClick: () => { 451 this.customDialogController.open(); 452 } 453 }) 454 } 455 .backgroundColor('#F5F5F5') 456 .height('100%') 457 .width('100%') 458 } 459 .height('100%') 460 } 461} 462``` 463## 参考 464- [警告弹窗](../application-dev/reference/apis-arkui/arkui-ts/ts-methods-alert-dialog-box.md) 465- [日期滑动选择器弹窗](../application-dev/reference/apis-arkui/arkui-ts/ts-methods-datepicker-dialog.md) 466- [文本滑动选择器弹窗](../application-dev/reference/apis-arkui/arkui-ts/ts-methods-textpicker-dialog.md) 467- [自定义弹窗](../application-dev/ui/arkts-common-components-custom-dialog.md)