弹窗
1.弹窗概述
弹窗是应用开发需要实现的基础功能,通常用来展示用户当前需要或用户必须关注的信息或操作,可用于广告、中奖、警告、软件更新等与用户交互响应的操作。系统提供了四种不同的方式来实现自定义弹窗,分别是CustomDialog、promptAction、UIContext.getPromptAction、Navigation.Dialog,在开发业务时,需要结合每种弹窗的特点来选择弹窗。
2. promptAction 弹窗
API version 9开始推出promptAction弹窗,支持了UI元素复用机制@Builder,但依赖UI组件。
2.1 导入模块
import { promptAction } from '@kit.ArkUI';
2.2 Toast弹窗
Toast弹窗是一种文本提示框;
Button("弹出Toast").onClick(() => {
promptAction.showToast({ message: "我是吐司🍞" })
})
2.3 自定义弹窗
通过promptAction.openCustomDialog打开自定义弹窗,弹窗宽度在设备竖屏时默认为所在窗口宽度 - 左右margin(16vp,设备为2in1时为40vp),最大默认宽度为400vp。
promptAction.openCustomDialog打开后,返回Promise表示弹窗的id,关闭弹窗时使用。
import { promptAction } from '@kit.ArkUI'
@Entry
@Component
struct Index {
@State customDialogId: number = 0
build() {
Column({ space: 10 }) {
Button("打开自定义弹窗")
.onClick(() => {
promptAction.openCustomDialog({
builder: () => this.CustomDialogBuilder(),
}).then((dialogId: number) => {
this.customDialogId = dialogId
})
})
}
.height('100%')
.width('100%')
}
@Builder
CustomDialogBuilder() {
CustomDialogComponent({ dialogId: this.customDialogId })
}
}
@Component
struct CustomDialogComponent {
@Prop dialogId: number
build() {
Column() {
Text("我是自定义弹窗")
Row() {
Button("取消").onClick(() => {
promptAction.closeCustomDialog(this.dialogId)
console.log("点击了取消按钮")
})
Button("确定").onClick(() => {
promptAction.closeCustomDialog(this.dialogId)
console.log("点击了确定按钮")
})
}
}
}
}
2.4 弹窗样式
通过promptAction.openCustomDialog打开弹窗,在指定弹窗UI的同时可以自定义样式,如:弹窗宽、高、背景、边框、蒙层、对齐方式、阴影等。
Button("打开自定义弹窗")
.onClick(() => {
promptAction.openCustomDialog({
builder: () => this.CustomDialogBuilder(),
keyboardAvoidMode: KeyboardAvoidMode.DEFAULT, //是否避让软键盘(DEFAULT避让,NONE不避让)
autoCancel:false, //点击遮罩层不自动关闭
alignment:DialogAlignment.Bottom, //对齐方式
backgroundColor:Color.White,
offset: { dx: 5, dy:0 }, //偏移量
isModal:false, //是否为模态窗口(true有蒙层,false无蒙层)
maskColor:"#ddeedd", //蒙层颜色
cornerRadius: 20, //圆角
width: '80%', //宽度
height: 100, //高度
borderWidth: 2, //边框宽度
borderStyle: BorderStyle.Dashed, //边框样式
borderColor: Color.Blue, //边框颜色
shadow: { //阴影
radius: 50, //圆角
color: Color.Green, //颜色
offsetX: 30, //向右偏移
offsetY: -30 //向下偏移
},
}).then((dialogId: number) => {
this.customDialogId = dialogId
})
})
2.5 弹窗最佳实践
- 先封装弹窗内容和样式
@Component
export struct DialogComponent {
build() {
Column() {
Text("🚀这是一个对话框")
.fontSize(20)
Button('确定')
.onClick(() => {
let dialogId = AppStorage.get<number>('dialogId');
promptAction.closeCustomDialog(dialogId)
})
}
.width("100%")
.height("100%")
.justifyContent(FlexAlign.Center)
}
}
- 在需要使用弹窗的页面中使用@Builder引入弹窗组件。
import { promptAction } from "@kit.ArkUI"
@Entry
@Component
struct UIContextPromptActionDialogPage {
build() {
Column() {
Button('打开弹窗')
.onClick(() => {
promptAction.openCustomDialog({
builder: () => this.DialogBuilder(),
alignment: DialogAlignment.Center,
height:200,
maskColor: 'rgba(0, 0, 0, 0.2)',
}).then((dialogId: number) => {
AppStorage.setOrCreate('dialogId', dialogId);
})
})
}
.height('100%')
.width('100%')
}
@Builder
DialogBuilder() {
DialogComponent()
}
}
3. UIContext.getPromptAction 弹窗
UIContext.getPromptAction()弹窗,基于promptAction弹窗演进而来,支持全局自定义弹窗,不依赖UI组件,依赖UIContext,支持在非页面文件中使用,弹窗内容支持动态修改,支持自定义弹窗圆角半径、大小和位置,适合在与页面解耦的全局弹窗、自定义弹窗显示和退出动画等场景下使用。
3.1 快速入门
import { ComponentContent, PromptAction, UIContext } from '@kit.ArkUI';
//UI上下文的对象(你可以理解为是一个管理UI的工具箱,它提供了很多函数可以对UI进行操作,如弹窗、动画等)
let ctx: UIContext | undefined = undefined
//弹窗内容
let componentContent: ComponentContent<Object> | undefined = undefined
//PromptAction提供了多种函数用于控制弹窗的显示、隐藏、更新
let promptAction: PromptAction | undefined = undefined
@Entry
@Component
struct Index {
aboutToAppear(): void {
ctx = this.getUIContext()
componentContent = new ComponentContent(
ctx,
wrapBuilder(buildText),
"HarmonyOS"
);
promptAction = ctx.getPromptAction()
}
build() {
Row() {
Column() {
Button("打开弹窗")
.margin({ top: 50 })
.onClick(() => {
//打开弹窗
promptAction?.openCustomDialog(
componentContent,
{
alignment: DialogAlignment.Center, //弹窗的位置
offset: {
//弹窗的偏移量
dx: 0,
dy: 50
}
})
})
}
.width('100%')
.height('100%')
}
.height('100%')
}
}
@Builder
function buildText(text: string) {
Column() {
Text(text)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.margin({ bottom: 36 })
Button("关闭").onClick(() => {
promptAction?.closeCustomDialog(componentContent)
})
}.backgroundColor(Color.White)
.borderRadius(10)
.padding(10)
}
3.2 封装弹窗参数
通过上面的快速入门案例我们发现,UIContext.getPromptAction弹窗依赖三个参数,分别是UIContext、ComponentContent、promptAction.BaseDialogOptions,我们可以使用一个类来封装这三个参数,方便在任何位置进行弹窗操作。
import { promptAction } from '@kit.ArkUI';
//单独写一个控制窗体打开与关闭的工具类
export class PromptActionUtils {
private static uicontext: UIContext //窗体的上下文(窗体的持有者)
private static componentContent: ComponentContent<Object> //代表窗体的UI和数据
private static options: promptAction.BaseDialogOptions //窗体的UI样式
public static setContext(context: UIContext) {
PromptActionUtils.uicontext = context;
}
public static setComponentContent(componentContent: ComponentContent<Object>) {
PromptActionUtils.componentContent = componentContent;
}
public static setOptions(options: promptAction.BaseDialogOptions) {
PromptActionUtils.options = options;
}
//打开弹窗
public static openDialog() {
if (PromptActionUtils.componentContent != null) {
if (PromptActionUtils.options) {
PromptActionUtils.uicontext.getPromptAction()
.openCustomDialog(PromptActionUtils.componentContent, PromptActionUtils.options)
} else {
PromptActionUtils.uicontext.getPromptAction()
.openCustomDialog(PromptActionUtils.componentContent)
}
}
}
//关闭弹窗
public static closeDialog() {
if (PromptActionUtils.componentContent != null) {
PromptActionUtils.uicontext.getPromptAction().closeCustomDialog(PromptActionUtils.componentContent)
}
}
//更新弹窗
public static updateDialog(componentContent: ComponentContent<Object>, options?: promptAction.BaseDialogOptions) {
if (componentContent != null && options !== null) {
PromptActionUtils.componentContent = componentContent
PromptActionUtils.options = options ?? PromptActionUtils.options
PromptActionUtils.uicontext.getPromptAction()
.updateCustomDialog(PromptActionUtils.componentContent, PromptActionUtils.options)
}
}
}
3.3 自定义弹窗
@Entry
@Component
struct PromptActionPage {
// 在页面显示的时候对窗体UI、数据、样式出初始化
paramOptions = new ParamOptions("李四王五赵六", 30)
aboutToAppear(): void {
console.log("学习学习学习学习学习学习学习")
PromptActionUtils.setContext(this.getUIContext())
PromptActionUtils.setComponentContent(new ComponentContent(this.getUIContext(), wrapBuilder(MyDialog),
this.paramOptions))
PromptActionUtils.setOptions({
isModal: true, //是否有蒙板
alignment: DialogAlignment.Center //窗体位置
})
}
build() {
Column() {
Button("弹窗")
.onClick(() => {
PromptActionUtils.openDialog()
// setTimeout(()=>{
// console.log("更新弹窗")
// PromptActionUtils.updateDialog({alignment:DialogAlignment.BottomEnd})
// },2000)
})
Button("更新")
.onClick(() => {
//this.paramOptions = new ParamOptions("哈哈哈哈",50)
PromptActionUtils.updateDialog(
new ComponentContent(this.getUIContext(), wrapBuilder(MyDialog), new ParamOptions("看看坎坎坷坷", 50)),
{alignment:DialogAlignment.Bottom}
)
})
}
.height('100%')
.width('100%')
}
}
//配置弹窗的数据
class ParamOptions {
text: string //文本
size: number //大小
constructor(text: string //文本
, size: number //文本大小
) {
this.text = text
this.size = size
}
}
@Builder
function MyDialog(param: ParamOptions) {
Column() {
Text(`${param.text}`)
.fontSize(`${param.size}`)
Button("关闭")
.onClick(() => {
PromptActionUtils.closeDialog()
})
}.width(300)
.height(200)
.backgroundColor(Color.White)
.borderRadius(10)
}
4.固定样式弹窗
固定样式弹出框采用固定的布局格式,这使得开发者无需关心具体的显示布局细节,只需输入所需显示的文本内容,从而简化了使用流程,提升了便捷性。
4.1 菜单弹窗
import {promptAction} from '@kit.ArkUI';
@Entry
@Component
struct Index {
@State city: string = ''
buttons: [promptAction.Button, promptAction.Button?, promptAction.Button?, promptAction.Button?, promptAction.Button?, promptAction.Button?] =
[
{
text: 'item1',
color: '#666666'
},
{
text: 'item2',
color: '#000000'
},
{
text: 'item3',
color: '#000000'
},
]
build() {
Column() {
Button("菜单弹窗")
.onClick(() => {
try {
promptAction.showActionMenu({
title: '菜单弹窗标题',
buttons: this.buttons
})
.then(data => {
console.info('菜单弹窗显示成功: ' + data.index);
this.city = this.buttons[data.index]?.text as string
})
.catch((err: Error) => {
console.error('showActionMenu error: ' + err);
})
} catch (error) {
}
})
Text(`${this.city}`)
}
.height('100%')
.width('100%')
}
}
4.2 普通对话框
import { promptAction } from '@kit.ArkUI';
@Entry
@Component
struct Index {
build() {
Column() {
Button("普通对话框")
.onClick(() => {
try {
promptAction.showDialog({
title: '普通对话框',
message:"这是一个普通的对话框",
buttons: [
{
text:"取消",
color:"#000000"
},
{
text:'确认',
color:"#000000"
}
]
})
.then(data => {
if (data.index===0) {
promptAction.showToast({message:"点取消了"})
}else if (data.index ===1){
promptAction.showToast({message:"点确定了"})
}
})
.catch((err:Error) => {
console.error('showDialog error: ' + err);
})
} catch (error) {
}
})
}
.height('100%')
.width('100%')
}
}
4.3 日历选择器
// xxx.ets
@Entry
@Component
struct CalendarPickerDialogExample {
private selectedDate: Date = new Date()
build() {
Column() {
Button("日历选择器")
.onClick(() => {
CalendarPickerDialog.show({
selected: this.selectedDate, //当前选中的日期
acceptButtonStyle: { //确定按钮样式
fontColor: '#2787d9',
fontSize: '16fp',
backgroundColor: '#f7f7f7',
borderRadius: 10
},
cancelButtonStyle: { //取消按钮样式
fontColor: Color.Red,
fontSize: '16fp',
backgroundColor: '#f7f7f7',
borderRadius: 10
},
onAccept: (date: Date)=>{
console.log("点击确认按钮")
// 当弹出框再次弹出时显示选中的是上一次确定的日期
this.selectedDate = date
},
onCancel: ()=>{
console.log("点击取消按钮")
},
onChange:(date:Date)=>{
console.log("当前日期是:"+date.toLocaleDateString())
}
})
})
}.width('100%')
.height("100%")
}
}
4.4 日期滑动选择器
@Entry
@Component
struct DatePickerDialogExample {
@State selectTime: Date = new Date('2023-12-25T08:30:00');
build() {
Column() {
Button('日期滑动选择器')
.margin(30)
.onClick(() => {
this.getUIContext().showDatePickerDialog({
start: new Date("1969-1-1"), //开始日期
end: new Date("2100-12-31"), //结束日期
selected: this.selectTime, //选中的日期
lunar:false, //是否为农历,默认为false
lunarSwitch: false, //是否显示农历切换开关,默认为false
showTime: false, //是否显示时间
useMilitaryTime:true, //是否展示24小时制,默认为false
acceptButtonStyle:{ //确认按钮的样式
fontColor:Color.Black,
fontSize:12,
fontWeight:FontWeight.Bold,
backgroundColor:Color.Orange
},
cancelButtonStyle:{ //取消按钮的样式
fontColor:Color.Black,
fontSize:12,
fontWeight:FontWeight.Lighter,
backgroundColor:Color.Orange
},
onDateAccept: (value: Date) => { //点击确定按钮时触发
this.selectTime = value
console.info("DatePickerDialog:onAccept()" + JSON.stringify(value))
},
})
})
}.width('100%').margin({ top: 5 })
}
}
4.5 时间滑动选择器
// xxx.ets
@Entry
@Component
struct TimePickerDialogExample {
@State selectTime: Date = new Date('2023-12-25T08:30:00');
build() {
Column() {
Button('时间滑动选择器')
.margin(30)
.onClick(() => {
this.getUIContext().showTimePickerDialog({
selected: this.selectTime, //选中的时间
textStyle: { //设置所有选项中最上和最下两个选项的文本颜色、字号、字体粗细。
color: '#2787d9',
font: {
size: '14fp',
weight: FontWeight.Normal
}
},
disappearTextStyle:{ //设置所有选项中最上和最下两个选项的文本颜色、字号、字体粗细。
color: '#dedede',
font: {
size: '16fp',
weight: FontWeight.Normal
}
},
selectedTextStyle: { //设置选中项的文本颜色、字号、字体粗细。
color: '#004aaf',
font: {
size: '20fp',
weight: FontWeight.Bold
}
},
useMilitaryTime:true, //是否24小时制
acceptButtonStyle: {
fontColor: '#2787d9',
fontSize: '16fp',
backgroundColor: '#f7f7f7',
borderRadius: 10
},
cancelButtonStyle: {
fontColor: Color.Red,
fontSize: '16fp',
backgroundColor: '#f7f7f7',
borderRadius: 10
},
onAccept:(value:TimePickerResult)=>{
console.log("当前选中的时间为:"+value.hour+"时"+value.minute+"分")
}
})
})
}.width('100%').margin({ top: 5 })
}
}
4.6 文本滑动选择器
import { JSON } from '@kit.ArkTS';
@Entry
@Component
struct TextPickerDialogExample {
private fruits: TextCascadePickerRangeContent[] = [
{
text: '辽宁省',
children: [
{ text: '沈阳市', children: [{ text: '沈河区' }, { text: '和平区' }, { text: '浑南区' }] },
{ text: '大连市', children: [{ text: '中山区' }, { text: '金州区' }, { text: '长海县' }] }
]
},
{
text: '吉林省',
children: [
{ text: '长春市', children: [{ text: '南关区' }, { text: '宽城区' }, { text: '朝阳区' }] },
{ text: '四平市', children: [{ text: '铁西区' }, { text: '铁东区' }, { text: '梨树县' }] }
]
},
{
text: '黑龙江省',
children: [
{ text: '哈尔滨市', children: [{ text: '道里区' }, { text: '道外区' }, { text: '南岗区' }] },
{ text: '牡丹江市', children: [{ text: '东安区' }, { text: '西安区' }, { text: '爱民区' }] }
]
}
]
private select: number = 0;
build() {
Column() {
Button('文本滑动选择器')
.margin(30)
.onClick(() => {
this.getUIContext().showTextPickerDialog({
range: this.fruits,
selected: this.select,
onAccept: (result: TextPickerResult) => {
console.log(result.value[0]+result.value[1]+result.value[2])
}
})
})
}.width('100%').margin({ top: 5 })
}
}