【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)系统定制弹框,可以直接使用。

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

dart 复制代码
@Entry
@Component
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)

dart 复制代码
// 日历选择器弹窗示例 (CalendarPickerDialog)
@Entry
@Component
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)自定义弹框。

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

dart 复制代码
  @State message: string = "测试文本"
  /**
 * 自定义弹框布局
 * @param params
 */
@Builder
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弹出自定义弹框:

dart 复制代码
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。

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

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

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

dart 复制代码
import { promptAction } from '@kit.ArkUI'
import { BusinessError } from '@kit.BasicServicesKit'

/**
 * 气泡示例
 */
@Entry
@Component
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)
  }
}

三、源码示例

dart 复制代码
// 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
 */
@Builder
function buildText(params: Params) {
  Column() {
    Text(params.text)
      .fontSize(50)
      .fontWeight(FontWeight.Bold)
      .margin({ bottom: 36 })
    Button('Close')
      .onClick(() => {
        PromptActionClass.closeDialog()
      })
  }.backgroundColor('#FFF0F0F0')
}

/**
 * 首页
 */
@Entry
@Component
struct Index {
  @State message: string = "hello"
  private ctx: UIContext = this.getUIContext();

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

  @State 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%')
  }
}
相关推荐
程序猿阿伟5 分钟前
《Flutter社交应用暗黑奥秘:模式适配与色彩的艺术》
前端·flutter
rafael(一只小鱼)8 分钟前
黑马点评实战笔记
前端·firefox
weifont9 分钟前
React中的useSyncExternalStore使用
前端·javascript·react.js
初遇你时动了情13 分钟前
js fetch流式请求 AI动态生成文本,实现逐字生成渲染效果
前端·javascript·react.js
影子信息27 分钟前
css 点击后改变样式
前端·css
几何心凉1 小时前
如何使用 React Hooks 替代类组件的生命周期方法?
前端·javascript·react.js
小堃学编程1 小时前
前端学习(1)—— 使用HTML编写一个简单的个人简历展示页面
前端·javascript·html
晚秋大魔王2 小时前
OpenHarmony 开源鸿蒙南向开发——linux下使用make交叉编译第三方库——nettle库
linux·开源·harmonyos
hnlucky2 小时前
通俗易懂版知识点:Keepalived + LVS + Web + NFS 高可用集群到底是干什么的?
linux·前端·学习·github·web·可用性测试·lvs
懒羊羊我小弟2 小时前
使用 ECharts GL 实现交互式 3D 饼图:技术解析与实践
前端·vue.js·3d·前端框架·echarts