概述
在 HarmonyOS 开发中,弹窗是每个应用都会遇到的场景,其重要性不容忽视。一方面,弹窗可以作为一种即时反馈机制,向用户传递重要的信息或提示,如登录提示、网络请求状态、操作确认等。这些弹窗通常具有模态或半模态特性,能够暂时阻断用户的其他操作,确保用户能够注意到并处理这些信息。另一方面,弹窗还可以用于展示广告、推广内容或引导用户进行下一步操作。例如,在 App 首页或某些关键页面,通过弹窗展示全屏广告或引导用户参与某项活动,可以有效地提升用户参与度。为了方便开发者在 HarmonyOS 上高效使用不同的弹窗能力,DialogHub 解决方案应运而生。
DialogHub 作为 ArkUI 弹窗能力的解决方案,提供了以下功能特性:
页面级弹窗能力
确保弹窗与页面生命周期紧密绑定,页面销毁时自动清理弹窗资源。
在页面切换或导航时,自动检查并隐藏旧页面的弹窗。
弹窗管理能力
提供弹窗状态管理,区分弹窗是否正在显示、是否已关闭。
提供监听机制,允许开发者在弹窗状态变化时执行自定义逻辑,包括弹出、即将弹出、关闭、即将关闭 4 种状态。
简化创建弹窗流程
精简链式调用的 API 设计,确保常用弹窗可以通过简洁的语法创建。
提供默认配置,减少不必要的参数设置,提高调用效率。
自定义弹窗模板提升易用性
允许开发者自定义模板并保存到模板库中,便于后续复用。
层级管理、手势透传等多种自定义配置属性
提供更灵活的层级管理机制,允许开发者动态调整弹窗的 Z 轴顺序。
提供层级冲突的解决策略,如新旧置顶弹窗的解决策略。
允许开发者自定义手势透传的行为,如是否允许手势穿透弹窗作用到底层页面。
提供更多自定义属性,如弹窗的动画效果、背景颜色、圆角半径等。
弹窗刷新机制
提供属性值的动态更新机制,允许开发者在弹窗显示过程中修改属性。
本文主要以实际开发中的各项场景为例,介绍 DialogHub 的使用。
实现原理
- 弹窗能力**:**基于 ArkUI 框架中的 OverlayManager 和 BindSheet 能力实现。OverlayManager为弹窗提供一个可以覆盖在其他 UI 元素之上的显示层,而BindSheet则支持将弹窗与特定的页面或组件绑定,实现更精细的控制。
- **页面级弹窗控制:**通过UIObserver实时监听应用内的路由变化,当路由发生变化时,触发相应的回调,从而允许 DialogHub 根据当前页面的状态来决定是否显示或隐藏弹窗。
开发流程
前提
开发者参考DialogHub 使用说明进行安装配置。
开发者调用 init()接口并传入 UIContext 以初始化 DialogHub。
弹窗能力开发流程
通过 DialogHub 直接创建弹窗然后进行显示或者销毁。
获取弹窗构造器:
调用 DialogHub 的 getToast()等接口,获取不同类型的弹窗构造器 DialogBuilder。
配置弹窗内容:
调用 DialogBuilder 的 setContent()、setAnimation()等接口,配置弹窗的具体内容、动画效果、样式等。
创建弹窗实例:
调用 DialogBuilder 的 build() 接口,创建弹窗实例 InfDialog 对象。
显示与销毁弹窗:
调用 InfDialog 对象的 show() 方法显示弹窗。
调用 InfDialog 对象的 dismiss() 方法销毁弹窗。
模板复用能力开发流程
开发者自定义模板并注册到模板库中,便于后续复用。
创建弹窗模板构造器:
调用 DialogHub 的 createToastTemplate()等接口,创建不同类型弹窗的模板构造器 DialogTemplate。
配置模板内容:
调用 DialogTemplate 的 setContent()、setAnimation()等接口,配置模板的具体内容、动画效果、样式等。
注册模板:
调用 DialogTemplate 的 register() 接口,将配置好的模板注册并存储。
获取并使用弹窗模板:
调用 DialogHub 的 getToastTemplate()等接口,根据模板名称获取对应的 DialogBuilder。
然后按照弹窗能力开发流程中的步骤 2~4,使用 DialogBuilder 配置并显示弹窗。
说明
获取模板后配置的属性(如动画、位置等)只针对当前弹窗对象生效,不会修改模板内容。
(可选) 更新模板:
调用 DialogHub 的 updateToastTemplate()、updatePopupTemplate() 等接口,更新对应模板名称的配置,并重新注册。
(可选) 删除模板:
调用 DialogHub 的 removeTemplate() 接口,删除对应模板名称的弹窗模板。
(可选) 判断模板是否存在:
调用 DialogHub 的 isTemplateExist() 接口,判断指定模板名称的弹窗模板是否已被注册。
常见业务弹窗
纯文本有持续时间的提示窗
一个简单的文本 Toast 弹窗,到达指定时间后消失。setDuration()设置 Toast 持续时间。

指定位置弹窗的非模态弹窗
在屏幕底部弹出 SnakeBar,该弹窗可以响应用户点击跳转页面或者关闭弹窗。

会定时消失且带弹出动效的弹窗
实现一个定时弹窗,6s 自动关闭。
- 通过 setAnimation()设置弹窗弹出动效。
- 通过 dialog 实例的 updateContent(),定时动态刷新弹窗内容。

会避让键盘的弹窗
通过 setConfig()的 keyboardAvoidMode 可以配置避让模式,CustomKeyboardAvoidMode.CONTENT_AVOID 为弹窗内容避让。
requestFocusWhenShow 配置为 true,弹窗显示时,弹窗自动获焦。


指向选定组件的带箭头弹窗
通过 getPopup()构造 Popup 弹窗实例,setStyle()中 enableArrow、arrowOffset、arrowWidth、arrowHeight 可配置箭头属性;
setConfig()中 preferPlacement 可配置箭头偏向。
说明
绑定组件需要调用 setComponentTargetId(targetCompId),targetCompId 组件 id 标识确保唯一,否则会报错且弹窗位置异常。

点击蒙层自动关闭的弹窗
弹出此类型弹窗需要打开 isModal 蒙层开关,并将 autoDismiss 设置为 true

可主动关闭的弹窗
能够通过点击弹窗按钮关闭弹窗,设置弹窗 Content 时,调用 setOperableContent(),并将 DialogHub 的 Dismiss 事件作为参数传递给 Builder。

能够动态调整高度的底部弹窗
实现动态调整弹窗高度,不同高度展示不同弹窗内容。
- 获取 DialogHub 的 Sheet 类型弹窗实例
- 弹窗实例增加 Sheet 高度监听 onHeightDidChange(),当高度变化到一定程度,updateContent()刷新弹窗内容
说明
sheet 类型弹窗须调用 setComponentTargetId(targetCompId)以实现页面级弹窗,并且保证绑定的组件 id 存在。


应用感知弹窗的打开、关闭
- 对弹窗实例增加生命周期,拦截弹窗的展示与销毁。
- 直接获取弹窗状态


弹窗与周边的交互
弹窗存在时如何定义返回手势是退出页面或关闭弹窗
配置状态变量 backCloseDialog,设置 true 表示返回手势作用于弹窗,false 表示作用于页面。
在 onBackPressed()中拦截手势并选择是退出页面还是关闭最上层弹窗
用户可以透过弹窗内容操作页面
弹出 Toast 类型的弹窗,或者主动调用 setConfig()设置 passThroughGesture 为 true,可实现弹窗内容透传手势。

需要向页面返回数据的弹窗
给 Builder 参数传递修改页面数据的回调函数,在 Builder 里面进行调用。


父页面刷新正在展示的弹窗内容
修改 Builder 参数内容,再调用 updateContent()进行修改。

页面需要感知当前页面是否存在弹窗
DialogHub 注册页面弹窗数监听,当前页面弹窗数量发生变化会触发。
存在跳转链接的弹窗
点击弹窗上特定内容,跳转到其他页面。
router:在弹窗 Builder 里通过 router 模板跳转。
Navigation:将 pageInfos 传入弹窗 Builder,然后在弹窗里进行 push 页面。

折叠屏展开态不同位置的弹窗
弹窗默认在屏幕中间;通过设置弹窗偏移量可以在不同位置进行弹窗。
弹窗在左半屏:

弹窗在右半屏:

弹窗内容复用场景
通过自定义弹窗模版进行弹窗
- 创建弹窗模板
- 直接弹出模板
- 删除弹窗模板
- 随机修改弹窗模板背景色
- (可选)通过弹窗模板,定义本次弹出动画后弹出
定义一个可复用的弹窗
将弹窗实例对象记录,下次弹窗复用。
多个弹窗并存场景
新弹窗被已有弹窗抑制
弹窗 A 弹出时抑制弹窗 B 的弹出
可以通过弹窗 A 对象的 getStatus()方法获取弹窗 A 的状态,以判断是否允许弹窗 B 弹出。
当前页面存在弹窗时抑制弹窗 C 的弹出
通过调用 DialogHub 的 getCurrentPageDialogs()方法获取当前页面的弹窗数量,判断数量是否为 0,并据此控制弹窗 C 的弹出。
开发者可以控制弹窗层级实现弹窗的相互覆盖
设置弹窗层级 setLayerIndex()
设置置顶弹窗 OLD_FIRST (老置顶弹窗优先,新的置顶弹窗无法弹出)
设置置顶弹窗 NEW_FIRST (新弹窗优先,新的置顶弹窗弹出,老置顶弹窗被覆盖)
常见问题
如何处理弹窗的获焦问题
对于 Sheet 类别的弹窗,弹窗弹出后的焦点行为与系统 BindSheet 保持一致;
DialogHub 提供的其他类别弹窗,如 CustomDialg,在弹窗弹出时父页面的焦点默认不会转移到弹窗上。
开发者可以配置弹窗的 requestFocusWhenShow 属性实现:弹窗弹出时,将页面的焦点转移到弹窗中。进而实现会避让键盘的弹窗的效果。
Popup 绑定组件,id 报错
组件标识 id需要开发者保证唯一性。setComponentTargetId()设置绑定的组件 id 后,如果 id 有问题,会导致在 show 的时候报错且弹窗位置异常。
- id 不存在:不存在此 id 的节点,排查绑定组件是否设置该 id 属性。错误码 70000001。
调用 build()与 show()接口后,无法继续添加属性
调用 build()接口后返回的是 Dialog 实例,只提供更新配置的接口。
removeTemplate()后,使用模板创建的弹窗实例可以继续显示
删除模板不影响之前通过模板已经创建的弹窗的显示和相关调用。
调用 isTemplateExist()判断模板存在,getxxxTemplate 模板报错 50000003
模板创建和获取时,需要保证弹窗类型一致,否则无法获取模板并报错,错误码 50000003。
可在获取模板前调用 queryTemplate()查询模板的弹窗类型。
Toast 弹窗置顶策略
Toast 弹窗默认为置顶弹窗,且置顶冲突策略为 TopDialogPriority.NEW_FIRST。
键盘避让模式变化
在使用 DialogHub 进行弹窗后,会将页面键盘避让模式修改为 RESIZE,当页面无弹窗或者页面跳转时,避让模式还原。