当前位置: 首页 > news >正文

【HarmonyOS Next】鸿蒙应用弹框和提示气泡详解(一)

【HarmonyOS Next】鸿蒙应用弹框和提示气泡详解(一)

一、前言

应用开发中我们会很频繁的接触,弹框Dialog和提示气泡Toast的使用。

移动开发的同学会比较熟悉。不过在鸿蒙的响应式布局中,使用鸿蒙早期提供的弹框Dialog和提示气泡Toast,会很不方便。因为是和UI绑定在一起,在纯逻辑类文件中使用不了。

因为该历史问题,后续API迭代时,对鸿蒙的弹框Dialog和提示气泡Toast进行了升级。目前可以在纯逻辑类中使用,与UI进行了解耦。

迭代优化过程
从page界面UI上弹出 =》挂靠子窗口实现弹出=》UI框架层预留挂靠节点

在这里插入图片描述
根据迭代过程,我们可以发现,目前和UI强绑定的实现方式API已标注不推荐。

二、鸿蒙中的弹框使用

综上所述,目前鸿蒙HarmonyOS对于弹框和提示气泡,以及延申的组件(浮层,Popup,Menu, bindSheet, bindContentCover)

实现方案都是挂靠到UI框架的预留节点上,进行添加渲染。
1.弹框的实现
目前弹框有两种方式:
(1)系统定制弹框,可以直接使用。

系统定制弹框根据业务复杂度的不同,也有两种方式封装。
分别是基础弹框(警告弹框,列表弹窗):
在这里插入图片描述

在这里插入图片描述



struct AlertDialogTextPage {
  build() {
    Column() {
      Button('showAlertDialog')
        .margin(30)
        .onClick(() => {
          this.getUIContext().showAlertDialog(
            {
              title: 'title',
              message: 'text',
              autoCancel: true,
              alignment: DialogAlignment.Center,
              buttons: [{
                value: 'cancel',
                action: () => {
                  console.info('cancel')
                }
              },
                {
                  enabled: true,
                  defaultFocus: true,
                  style: DialogButtonStyle.HIGHLIGHT,
                  value: 'ok',
                  action: () => {
                    console.info('ok')
                  }
                }],
            }
          )
        })
    }
    .width('100%')
    .margin({ top: 5 })
  }
}

还有带业务性质的PickerDialog弹框:
日历选择器弹窗 (CalendarPickerDialog)
日期滑动选择器弹窗 (DatePickerDialog)
时间滑动选择器弹窗 (TimePickerDialog)
文本滑动选择器弹窗 (TextPickerDialog)

// 日历选择器弹窗示例 (CalendarPickerDialog)


struct PickerDialogTextPage {
  private selectedDate: Date = new Date('2024-04-23')

  build() {
    Column() {
      Button("Show CalendarPicker Dialog")
        .margin(20)
        .onClick(() => {
          console.info("CalendarDialog.show")
          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)=>{
              // 当弹出框再次弹出时显示选中的是上一次确定的日期
              this.selectedDate = date
            }
          })
        })
    }.width('100%')
  }
}

(2)自定义弹框。

需要创建节点对象,在节点对象中添加自定义弹框的布局:

   message: string = "测试文本"
  /**
 * 自定义弹框布局
 * @param params
 */

function buildText(params: Params) {
  Column() {
    Text(params.text)
      .fontSize(50)
      .fontWeight(FontWeight.Bold)
      .margin({ bottom: 36 })
    Button('Close')
      .onClick(() => {
        PromptActionClass.closeDialog()
      })
  }.backgroundColor('#FFF0F0F0')
}
  private contentNode: ComponentContent<Object> =
    new ComponentContent(this.ctx, wrapBuilder(buildText), new Params(this.message));

之后获取PromptAction对象,调用openCustomDialog弹出自定义弹框:

this.getContext().getPromptAction().openCustomDialog(PromptActionClass.contentNode)
        .then(() => {
          console.info('OpenCustomDialog complete.')
        })
        .catch((error: BusinessError) => {
          let message = (error as BusinessError).message;
          let code = (error as BusinessError).code;
          console.error(`OpenCustomDialog args error code is ${code}, message is ${message}`);
        })

当你需要修改自定义弹框的对齐方式,偏移量等,需要添加promptAction.BaseDialogOptions。

this.getContext().getPromptAction().openCustomDialog(PromptActionClass.contentNode,{ alignment: DialogAlignment.Top, offset: { dx: 0, dy: 50 } })
        .then(() => {
          console.info('OpenCustomDialog complete.')
        })

2.气泡的实现
Toast的使用很简单,目前只能支持自定义文本内容,气泡层级(默认在页面内,Top在页面上)

对齐方式和悬浮态。不能设置字体的大小和颜色。如果要自定义字体大小和颜色。需要使用自定义弹框或者Popup来实现。

import { promptAction } from '@kit.ArkUI'
import { BusinessError } from '@kit.BasicServicesKit'

/**
 * 气泡示例
 */


struct ToastTextPage {

  build() {
    Column() {
      Button('Show toast').fontSize(20)
        .onClick(() => {
          try {
            this.getUIContext().getPromptAction().showToast({
              message: '测试气泡',
              duration: 2000,
              showMode: promptAction.ToastShowMode.TOP_MOST
            });
          } catch (error) {
            let message = (error as BusinessError).message
            let code = (error as BusinessError).code
            console.error(`showToast args error code is ${code}, message is ${message}`);
          };
        })
    }
    .height('100%')
    .width('100%')
    .justifyContent(FlexAlign.Center)
  }
}

三、源码示例

// PromptActionClass.ets
import { BusinessError } from '@kit.BasicServicesKit';
import { ComponentContent, promptAction } from '@kit.ArkUI';
import { PromptAction, UIContext } from '@ohos.arkui.UIContext';

/**
 * 自定义弹框封装
 */
export class PromptActionClass {
  static ctx: UIContext;
  static contentNode: ComponentContent<Object>;
  static options: promptAction.BaseDialogOptions;

  static setContext(context: UIContext) {
    PromptActionClass.ctx = context;
  }

  static setContentNode(node: ComponentContent<Object>) {
    PromptActionClass.contentNode = node;
  }

  static setOptions(options: promptAction.BaseDialogOptions) {
    PromptActionClass.options = options;
  }

  static openDialog() {
    if (PromptActionClass.contentNode !== null) {
      PromptActionClass.ctx.getPromptAction().openCustomDialog(PromptActionClass.contentNode, PromptActionClass.options)
        .then(() => {
          console.info('OpenCustomDialog complete.')
        })
        .catch((error: BusinessError) => {
          let message = (error as BusinessError).message;
          let code = (error as BusinessError).code;
          console.error(`OpenCustomDialog args error code is ${code}, message is ${message}`);
        })
    }
  }

  static closeDialog() {
    if (PromptActionClass.contentNode !== null) {
      PromptActionClass.ctx.getPromptAction().closeCustomDialog(PromptActionClass.contentNode)
        .then(() => {
          console.info('CloseCustomDialog complete.')
        })
        .catch((error: BusinessError) => {
          let message = (error as BusinessError).message;
          let code = (error as BusinessError).code;
          console.error(`CloseCustomDialog args error code is ${code}, message is ${message}`);
        })
    }
  }

  static updateDialog(options: promptAction.BaseDialogOptions) {
    if (PromptActionClass.contentNode !== null) {
      PromptActionClass.ctx.getPromptAction().updateCustomDialog(PromptActionClass.contentNode, options)
        .then(() => {
          console.info('UpdateCustomDialog complete.')
        })
        .catch((error: BusinessError) => {
          let message = (error as BusinessError).message;
          let code = (error as BusinessError).code;
          console.error(`UpdateCustomDialog args error code is ${code}, message is ${message}`);
        })
    }
  }
}


class Params {
  text: string = ""

  constructor(text: string) {
    this.text = text;
  }
}

/**
 * 自定义弹框布局
 * @param params
 */

function buildText(params: Params) {
  Column() {
    Text(params.text)
      .fontSize(50)
      .fontWeight(FontWeight.Bold)
      .margin({ bottom: 36 })
    Button('Close')
      .onClick(() => {
        PromptActionClass.closeDialog()
      })
  }.backgroundColor('#FFF0F0F0')
}

/**
 * 首页
 */


struct Index {
   message: string = "hello"
  private ctx: UIContext = this.getUIContext();

  private contentNode: ComponentContent<Object> =
    new ComponentContent(this.ctx, wrapBuilder(buildText), new Params(this.message));

   handlePopup: boolean = false

  aboutToAppear(): void {
    PromptActionClass.setContext(this.ctx);
    PromptActionClass.setContentNode(this.contentNode);
    PromptActionClass.setOptions({ alignment: DialogAlignment.Top, offset: { dx: 0, dy: 50 } });
  }



  build() {
    Row() {
      Column() {
        /**
         * 显示自定义气泡 更新样式
         */
        Button("open dialog and update options")
          .margin({ top: 50 })
          .onClick(() => {
            PromptActionClass.openDialog()

            setTimeout(() => {
              PromptActionClass.updateDialog({
                alignment: DialogAlignment.Bottom,
                offset: { dx: 0, dy: -50 }
              })
            }, 1500)
          })

        /**
         * 显示自定义气泡 更新内容数据
         */
        Button("open dialog and update content")
          .margin({ top: 50 })
          .onClick(() => {
            PromptActionClass.openDialog()

            setTimeout(() => {
              this.contentNode.update(new Params('update'))
            }, 1500)
          })

        /**
         * 气泡菜单
         */
        Button('PopupOptions')
          .onClick(() => {
            this.handlePopup = !this.handlePopup
          })
          .bindPopup(this.handlePopup, {
            message: 'This is a popup with PopupOptions',
          })

        /**
         * 提示气泡
         */
        Button('show Toast')
          .onClick(() => {
            let promptAction: PromptAction = this.ctx.getPromptAction();
            try {
              promptAction.showToast({
                message: 'Message Info',
                duration: 2000
              });
            } catch (error) {
              let message = (error as BusinessError).message;
              let code = (error as BusinessError).code;
              console.error(`showToast args error code is ${code}, message is ${message}`);
            };
          })

        /**
         * 系统弹框
         */
        Button('show Toast')
          .onClick(() => {
            let promptAction: PromptAction = this.ctx.getPromptAction();
            try {
              promptAction.showToast({
                message: 'Message Info',
                duration: 2000
              });
            } catch (error) {
              let message = (error as BusinessError).message;
              let code = (error as BusinessError).code;
              console.error(`showToast args error code is ${code}, message is ${message}`);
            };
          })
      }
      .width('100%')
      .height('100%')
    }
    .width('100%')
    .height('100%')
  }
}

相关文章:

  • 无人机避障——XTDrone中运行VINS-Fusion+Ego-planner进行路径规划
  • iOS插件,Theos环境搭建与XM文件开发指南(完善版本)
  • 【论文阅读】多模态——CLIPasso
  • 从0到1,带你开启TypeScript的奇妙之旅
  • 【VUE2】第二期——生命周期及工程化
  • STM32 ——系统架构
  • 力扣热题 100:二叉树专题进阶题解析(后7道)
  • Kubernetes(K8s)集群中使用 GPU
  • MySQL中的脏读与幻读
  • html网页模板免费免费html网页模板源代码
  • 【Java---数据结构】栈和队列【Stack Queue】
  • Python数据分析之数据处理与分析
  • 重生之我在学Vue--第5天 Vue 3 路由管理(Vue Router)
  • AI 帮我精准定位解决 ReferenceError: process is not defined (文末附AI名称)
  • mobaxterm,闪退处理方法
  • 一学就会的深度学习基础指令及操作步骤(3)模型训练验证
  • FPGA|Verilog-自己写的SPI驱动
  • 【测试框架篇】单元测试框架pytest(4):assert断言详解
  • SpringBoot(1)——创建SpringBoot项目的方式
  • 【Vue3】详细探究 watch ref 数组不生效的问题
  • 苏丹港持续遭无人机袭击,外交部:呼吁各方保护民用设施和平民安全
  • 聚焦智能浪潮下的创业突围,“青年草坪创新创业湃对”走进北杨人工智能小镇
  • 中国田径巡回赛西安站完赛:男子跳远石雨豪夺冠
  • 江苏疾控:下设部门无“病毒研究所”,常荣山非本单位工作人员
  • 竞彩湃|英超欧冠悬念持续,纽卡斯尔诺丁汉能否拿分?
  • 2024年全国博物馆接待观众14.9亿人次