ArkUI框架之promptAction弹窗

弹窗

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 弹窗最佳实践

在这里插入图片描述

  1. 先封装弹窗内容和样式
@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)
  }
}
  1. 在需要使用弹窗的页面中使用@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 })
  }
}
<think>好的,我现在需要帮助用户了解如何在HarmonyOS中使用PromptAction实现多弹框功能。根据用户提供的引用内容,我需要结合API版本10及以上的功能,特别是可能涉及API 12中的openCustomDialog方法。首先,我需要确认用户的需求:他们希望同时或顺序显示多个弹框,可能需要自定义弹框,而不仅仅是默认的toast。 首先,我应该回顾用户提供的引用内容。引用[1]提到从API 10开始,可以使用UIContext的getPromptAction方法获取PromptAction对象。引用[2]展示了showToast的基本用法,而引用[3]指出在API 12中PromptAction支持了openCustomDialog,允许全局自定义弹框。这说明用户可能需要使用自定义弹框来实现多弹框功能。 接下来,我需要考虑如何实现多弹框。可能需要分步骤来展示如何创建多个弹窗,比如先显示一个toast,然后接着显示另一个弹窗,或者同时显示多个自定义弹窗。需要注意的是,弹窗的显示可能需要异步处理,比如在第一个弹窗关闭后再显示第二个,或者使用回调函数来控制流程。 然后,我需要检查示例代码的正确性。比如,使用showToast和showDialog的组合,或者多个showDialog的调用。同时,必须确保代码符合API版本的要求,比如API 12中的openCustomDialog。此外,代码中需要处理错误,使用try...catch块来捕获可能的异常,如用户提供的引用中的示例所示。 另外,用户可能需要了解如何管理多个弹窗的显示顺序,如何避免弹窗重叠,或者如何自定义它们的样式和位置。根据引用[3],openCustomDialog允许更多的自定义选项,这可能包括多个弹窗的不同配置。 我还需要确保代码示例符合用户提到的格式要求,比如行内数学表达式用$...$,独立公式用$$...$$,但在这个问题中可能不需要数学公式。不过,代码块需要用正确的语法,比如使用三个反引号包裹Python代码,但在这里用户的问题是关于HarmonyOSJavaScript/TypeScript代码,所以应该使用相应的语言标识。 最后,生成相关问题时要围绕PromptAction的多弹窗实现,可能涉及异步控制、自定义样式、事件处理等。例如,如何确保弹窗按顺序显示,如何处理用户交互,如何在不同API版本中兼容等。 总结步骤: 1. 展示使用showToast和showDialog的基本示例。 2. 提供API 12中使用openCustomDialog的自定义弹窗示例。 3. 演示如何顺序显示多个弹窗,可能使用回调或Promise。 4. 处理错误和异常情况。 5. 生成相关问题,覆盖更多细节和高级用法。</think>在HarmonyOS中实现多弹框功能,可以通过组合使用`showToast`和`openCustomDialog`方法(API 12新增),结合异步控制实现多弹窗的按序展示。以下是具体实现示例: --- ### 1. **基础弹窗组合示例** ```typescript import promptAction from '@ohos.promptAction'; // 示例1:先显示Toast,再弹出对话框 try { // 显示Toast提示 promptAction.showToast({ message: '操作成功', duration: 2000 }); // 2秒后显示对话框 setTimeout(() => { promptAction.showDialog({ title: '确认操作', message: '是否继续下一步?', buttons: [ { text: '取消', color: '#666666' }, { text: '确定', color: '#007DFF' } ] }).then((result) => { if (result.index === 1) { // 用户点击确定后的逻辑 } }).catch((error) => { console.error(`弹窗异常: ${error.code}, ${error.message}`); }); }, 2000); } catch (error) { console.error(`弹窗初始化失败: ${error.code}, ${error.message}`); } ``` --- ### 2. **API 12自定义多弹窗示例** 通过`openCustomDialog`实现完全自定义的弹窗层级控制: ```typescript // 示例2:嵌套自定义弹窗 try { // 第一个自定义弹窗 promptAction.openCustomDialog({ builder: (parent: View) => { // 自定义弹窗内容 let dialogContent = new Text(parent.getContext()); dialogContent.setText("这是第一个弹窗"); let button = new Button(parent.getContext()); button.setText("打开第二个弹窗"); button.onClick(() => { // 关闭当前弹窗后打开第二个弹窗 promptAction.dismissCurrentDialog(); promptAction.openCustomDialog({ builder: (parent: View) => { let secondDialog = new Text(parent.getContext()); secondDialog.setText("这是嵌套弹窗"); } }); }); } }); } catch (error) { console.error(`自定义弹窗失败: ${error.code}, ${error.message}`); } ``` --- ### 关键点说明 1. **异步控制**:通过`setTimeout`或按钮事件回调控制弹窗触发时序[^2] 2. **自定义层级**:API 12的`openCustomDialog`支持完全自定义UI组件,可通过`dismissCurrentDialog`管理弹窗堆栈[^3] 3. **错误处理**:所有弹窗操作需包裹在`try-catch`中以捕获权限异常或参数错误[^1] ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值