写点什么

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

作者:GeorgeGcs
  • 2025-03-20
    上海
  • 本文字数:5259 字

    阅读完需:约 17 分钟

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

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

一、前言

在应用开发中,弹框 Dialog 和提示气泡 Toast 使用频繁,移动开发同学较为熟悉。但在鸿蒙响应式布局里,早期的弹框 Dialog 和提示气泡 Toast 因与 UI 紧密绑定,在纯逻辑类文件中无法使用。


后来 API 迭代对其升级,如今可在纯逻辑类中使用,实现与 UI 解耦。

迭代优化过程

从 page 界面 UI 上弹出 => 挂靠子窗口实现弹出 => UI 框架层预留挂靠节点


由此可见,与 UI 强绑定的实现方式 API 已不推荐。


二、鸿蒙中的弹框使用

目前鸿蒙 HarmonyOS 对于弹框、提示气泡及相关延申组件(浮层,Popup,Menu, bindSheet, bindContentCover),都是挂靠到 UI 框架预留节点进行添加渲染。

1. 弹框的实现

弹框有两种方式:

(1) 系统定制弹框,可直接使用

系统定制弹框依业务复杂度有两种封装方式:基础弹框(警告弹框,列表弹窗)



@Entry@Componentstruct 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)@Entry@Componentstruct 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) 自定义弹框

创建节点对象并添加自定义弹框布局:


@State message: string = "测试文本"  /** * 自定义弹框布局 * @param params */@Builderfunction 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. 气泡的实现

气泡有对齐方式和悬浮态设置,但不能设置字体大小和颜色。自定义字体大小和颜色需用自定义弹框或 Popup。


import { promptAction } from '@kit.ArkUI'import { BusinessError } from '@kit.BasicServicesKit'
/** * 气泡示例 */@Entry@Componentstruct 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 */@Builderfunction buildText(params: Params) { Column() { Text(params.text) .fontSize(50) .fontWeight(FontWeight.Bold) .margin({ bottom: 36 }) Button('Close') .onClick(() => { PromptActionClass.closeDialog() }) }.backgroundColor('#FFF0F0F0')}
/** * 首页 */@Entry@Componentstruct 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%') }}
复制代码


发布于: 刚刚阅读数: 7
用户头像

GeorgeGcs

关注

路漫漫其修远兮,吾将上下而求索。 2024-12-24 加入

历经腾讯,宝马,研究所,金融。 待过私企,外企,央企。 深耕大应用开发领域十年。 OpenHarmony,HarmonyOS,Flutter,H5,Android,IOS。 目前任职鸿蒙应用架构师。 HarmonyOS官方认证创作先锋

评论

发布
暂无评论
【HarmonyOS Next】鸿蒙应用弹框和提示气泡详解(一)_ios_GeorgeGcs_InfoQ写作社区