X叶域Q的主页-鸿蒙开发者社区-51CTO.COM
自定义选择弹窗
自定义弹窗(CustomDialog)是一种十分实用的交互组件,它能够让开发者根据具体的业务场景,灵活地为用户呈现各种选择、提示等交互界面。
整体分为一个界面,三个弹窗,一个按钮组件

1. 定义要选择的数据类型和弹窗控制
首先,我们需要定义相关的数据类型以及用于控制弹窗的变量。在代码中,通过 @State
装饰器定义了两个重要的变量:
1 2 3 4 5
|
@State goodsSelect: string = "弹窗选择"
@State dialogController: CustomDialogController | null = null;
|
2. 绑定事件,点击打开弹窗
为了让用户能够触发弹窗的显示,需要将打开弹窗的操作与某个用户交互行为进行绑定,常见的就是点击事件。在代码示例中,使用了一个 Text
组件,并对其添加了点击事件处理逻辑。
1 2 3 4 5 6 7 8 9 10 11 12 13
| Text(this.goodsSelect) .fontSize(50) .fontWeight(FontWeight.Bold) .onClick(() => { this.dialogController = new CustomDialogController({ builder: PickerDialog({ goodsSelect: this.goodsSelect }) }) if (this.dialogController != null) { this.dialogController.open() } })
|
3. 自定义弹窗组件

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
|
@CustomDialog struct PickerDialog { @Link goodsSelect: string; controller: CustomDialogController; build() { MyTextPickerDialog({ confirm: (selectData: string) => { this.goodsSelect = selectData this.controller.close(); }, cancel: () => { this.controller.close(); } }) .height(300) } }
|
具体弹窗样式具体实现,可以设计不任意的UI界面实现
下面代码中使用了@ohos.events.emitter (Emitter)实现组件间通信,aboutToAppear自定义组件的生命周期中开始监听
下面主要是自定义了个统一的按钮组件,为了点击按钮组件(上图三个界面的按钮都用的同一块代码)的按钮触发不同界面的回调函数,具体实现见下方
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| import { MyButton } from './MyButton'; import { emitter } from '@kit.BasicServicesKit';
@Component export struct MyTextPickerDialog { @State textPikerDialogId: string = "textPikerDialog"; private fruits: string[] = ['apple1', 'orange2', 'peach3', 'grape4'] private select: number = 0; confirm: ((selectData: string) => void) | undefined = undefined cancel: (() => void) | undefined = undefined aboutToAppear(): void { emitter.once(`${this.textPikerDialogId}确定`, () => { if(this.confirm && this.select != -1){ this.confirm(this.fruits[this.select]) } }) emitter.once(`${this.textPikerDialogId}取消`, () => { if(this.cancel){ this.cancel() } }) } build() { Column() { Text("自定义文本选择组件")
TextPicker({ range: this.fruits, selected: this.select }) .onChange((value: string | string[], index: number | number[]) => { this.select = index as number }) .disappearTextStyle({color: Color.Red, font: {size: 15, weight: FontWeight.Lighter}}) .textStyle({color: Color.Black, font: {size: 20, weight: FontWeight.Normal}}) .selectedTextStyle({color: Color.Blue, font: {size: 30, weight: FontWeight.Bolder}}) MyButton({buttonId: this.textPikerDialogId}) } .height('100%') .width('100%') } }
|
通用按钮组件,自定义通用的按钮用于不同的界面,用emtter区分是哪个界面点击
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| import emitter from '@ohos.events.emitter'; @Component export struct MyButton { buttonId: string = ""; build() { Row() { Button("取消", { type: ButtonType.Normal }) .height(40) .width("43%") .backgroundColor(Color.Brown) .fontColor(Color.White) .fontSize(18) .borderRadius(5) .onClick(() => { let eventData: emitter.EventData = { data: {"id": "自定义传输数据"} }; emitter.emit(`${this.buttonId}取消`, eventData); })
Button("确定", { type: ButtonType.Normal }) .height(40) .width("43%") .fontColor(Color.Blue) .linearGradient({ direction: GradientDirection.Right, colors: [["#02edff", 0.0],["#1281ff", 1.0]] }) .fontSize(18) .borderRadius(5) .onClick(() => { let eventData: emitter.EventData = { data: {"id": "自定义传输数据"} }; emitter.emit(`${this.buttonId}确定`, eventData); })
} .justifyContent(FlexAlign.SpaceAround) .padding({left: 12, right: 12}) .width("100%") } }
|
能看出上面的弹窗是基于页面做固定定位的,位置无法变换,下面使用半模态转场实现半模态弹窗,更灵活的满足UI设计
1. 数据定义
用变量控制半模态弹窗的弹出
1 2 3 4 5 6
| @State timeSelect: Date = new Date(); @State timeShow: boolean = false;
@State placeSelect: string = "地点选择"; @State placeShow: boolean = false;
|
2. 弹窗绑定
给组件绑定半模态页面(一个组件不能绑定多个,会乱弹)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| Text(`${this.timeSelect.getHours()}小时${this.timeSelect.getMinutes()}分钟`) .fontSize(50) .fontWeight(FontWeight.Bold) .bindSheet(this.timeShow, this.timeSelectBuilder(),{ width: "100%", maskColor: 'rgba(125, 125, 125, 0.5)', showClose: false, height: '40%', mode: SheetMode.EMBEDDED, shouldDismiss: () => { this.timeShow = false; } }) .onClick(() => { this.timeShow = true; }) Text(this.placeSelect) .fontSize(50) .fontWeight(FontWeight.Bold) .bindSheet(this.placeShow, this.placeSelectBuilder(),{ width: "100%", maskColor: 'rgba(125, 125, 125, 0.5)', showClose: false, height: '30%', mode: SheetMode.EMBEDDED, shouldDismiss: () => { this.placeShow = false; } }) .onClick(() => { this.placeShow = true; })
|
3. 自定义@Builder
两种不同的方式实现将选择的数据同步到主界面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @Builder timeSelectBuilder(){ TimeBinSheet({ isShow: this.timeShow, timeSelect: this.timeSelect }) } @Builder placeSelectBuilder(){ PlaceBinSheet({ isShow: this.placeShow, confirm: (city: string) => { this.placeSelect = city; } }) }
|
下面代码可以灵活修改实现自定义弹窗,重点是确定后的数据修改

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| import { MyButton } from './MyButton'; import { emitter } from '@kit.BasicServicesKit';
@Component export struct TimeBinSheet { @Link isShow: boolean; @Link timeSelect: Date; @State time: Date = new Date() @State TimeBinSheetId: string = "TimeBinSheet"
aboutToAppear(): void { emitter.once(`${this.TimeBinSheetId}确定`, () => { this.timeSelect = this.time; this.isShow = false; }) emitter.once(`${this.TimeBinSheetId}取消`, () => { this.isShow = false; }) }
build() { Column() { Text("自定义时间选择组件") TimePicker({ selected: this.timeSelect, format: TimePickerFormat.HOUR_MINUTE, }) .useMilitaryTime(true) .onChange((value: TimePickerResult) => { this.time.setHours(value.hour, value.minute) console.info('select current date is: ' + JSON.stringify(value)) }) .disappearTextStyle({color: "#F6F6F6", font: {size: 18, weight: FontWeight.Lighter}}) .textStyle({color: "#858585", font: {size: 18, weight: FontWeight.Normal}}) .selectedTextStyle({color: "#000000", font: {size: 18}}) .width("76%") .height("74%") MyButton({buttonId: this.TimeBinSheetId}) } .height('100%') .width('100%') } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| import { MyButton } from './MyButton'; import { emitter } from '@kit.BasicServicesKit';
@Component export struct PlaceBinSheet { PlaceBinSheetId: string = "PlaceBinSheet" @Link isShow: boolean; cityList: Array<string> = ["北京", "南京", "深圳", "厦门"];
@State selectCity: number = -1; confirm: ((city: string) => void) | undefined = undefined; aboutToAppear(): void { emitter.once(`${this.PlaceBinSheetId}确定`, () => { if(this.confirm && this.selectCity != -1){ this.confirm(this.cityList[this.selectCity]); } this.isShow = false; }) emitter.once(`${this.PlaceBinSheetId}取消`, () => { this.isShow = false; }) } build() { Column() { Text("自定义地点选择组件") Row(){ ForEach(this.cityList,(item: string, index)=> { Button({ type: ButtonType.Normal, stateEffect: true }) { Text(item) .fontSize(14) .fontColor(this.selectCity === index ? Color.Black : Color.White) } .height(40) .width('23%') .borderRadius(4) .backgroundColor(this.selectCity === index ? Color.Red : Color.Blue) .onClick(() => { this.selectCity = index }) }) } .width("80%") .justifyContent(FlexAlign.SpaceAround) .margin(20)
MyButton({buttonId: this.PlaceBinSheetId}) } .height('100%') .width('100%') } }
|