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

28、鸿蒙Harmony Next开发:不依赖UI组件的全局气泡提示 (openPopup)和不依赖UI组件的全局菜单 (openMenu)、Toast

目录

不依赖UI组件的全局气泡提示 (openPopup)

弹出气泡

创建ComponentContent

绑定组件信息

设置弹出气泡样式

更新气泡样式

关闭气泡

在HAR包中使用全局气泡提示

不依赖UI组件的全局菜单 (openMenu)

弹出菜单

创建ComponentContent

绑定组件信息

设置弹出菜单样式

更新菜单样式

关闭菜单

在HAR包中使用全局菜单

Toast

使用建议

即时反馈模式对比

创建即时反馈

显示关闭即时反馈


不依赖UI组件的全局气泡提示 (openPopup)

气泡提示(Popup)在使用时依赖绑定UI组件,否则无法使用。从API version 18开始,可以通过使用全局接口openPopup的方式,在无UI组件的场景下直接或封装使用,例如在事件回调中使用或封装后对外提供能力。

弹出气泡

通过openPopup可以弹出气泡。

promptAction.openPopup(contentNode, { id: targetId }, {enableArrow: true
}).then(() => {console.info('openPopup success');}).catch((err: BusinessError) => {console.error('openPopup error: ' + err.code + ' ' + err.message);})

创建ComponentContent

通过调用openPopup接口弹出其气泡,需要提供用于定义自定义弹出框的内容ComponentContent。其中,wrapBuilder(buildText)封装自定义组件,new Params(this.message)是自定义组件的入参,可以缺省,也可以传入基础数据类型。

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

如果在wrapBuilder中包含其他组件(例如:Popup、Chip组件),则ComponentContent应采用带有四个参数的构造函数constructor,其中options参数应传递{ nestingBuilderSupported: true }。

@Builder
export function buildText(params: Params) {Popup({// 类型设置图标内容icon: {image: $r('app.media.app_icon'),width: 32,height: 32,fillColor: Color.White,borderRadius: 10} as PopupIconOptions,// 设置文字内容title: {text: `This is a Popup title 1`,fontSize: 20,fontColor: Color.Black,fontWeight: FontWeight.Normal} as PopupTextOptions,// 设置文字内容message: {text: `This is a Popup message 1`,fontSize: 15,fontColor: Color.Black} as PopupTextOptions,// 设置按钮内容buttons: [{text: 'confirm',action: () => {console.info('confirm button click');},fontSize: 15,fontColor: Color.Black,},{text: 'cancel',action: () => {console.info('cancel button click');},fontSize: 15,fontColor: Color.Black},] as [PopupButtonOptions?, PopupButtonOptions?]})
}let contentNode: ComponentContent<Object> = new ComponentContent(uiContext, wrapBuilder(buildText), this.message, { nestingBuilderSupported: true });

绑定组件信息

通过调用openPopup接口弹出气泡,需要提供绑定组件的信息TargetInfo。若未传入有效的target,则无法弹出气泡。

let frameNode: FrameNode | null = this.ctx.getFrameNodeByUniqueId(this.getUniqueId());
let targetId = frameNode?.getChild(0)?.getUniqueId();

设置弹出气泡样式

通过调用openPopup接口弹出气泡,可以设置PopupCommonOptions属性调整气泡样式。

private options: PopupCommonOptions = { enableArrow: true };

更新气泡样式

通过updatePopup可以更新气泡的样式。支持全量更新和增量更新其气泡样式,不支持更新showInSubWindow、focusable、onStateChange、onWillDismiss和transition。

promptAction.updatePopup(contentNode, {enableArrow: false
}, true).then(() => {console.info('updatePopup success');}).catch((err: BusinessError) => {console.error('updatePopup error: ' + err.code + ' ' + err.message);})

关闭气泡

通过调用closePopup可以关闭气泡。

promptAction.closePopup(contentNode).then(() => {console.info('closePopup success');}).catch((err: BusinessError) => {console.error('closePopup error: ' + err.code + ' ' + err.message);})

由于updatePopup和closePopup依赖content来更新或者关闭指定的气泡,开发者需自行维护传入的content。

在HAR包中使用全局气泡提示

以下示例通过HAR包封装一个Popup,从而对外提供气泡的弹出、更新和关闭能力。

// library/src/main/ets/components/MainPage.etsimport { BusinessError } from '@kit.BasicServicesKit';
import { ComponentContent, TargetInfo, PromptAction } from '@kit.ArkUI';export class PromptActionClass {private promptAction: PromptAction | null = null;private contentNode: ComponentContent<Object> | null = null;private options: PopupCommonOptions | null = null;private target: TargetInfo | null = null;private isPartialUpdate: boolean = false;public setPromptAction(promptAction: PromptAction) {this.promptAction = promptAction;}public setContentNode(node: ComponentContent<Object>) {this.contentNode = node;}public setTarget(target: TargetInfo) {this.target = target;}public setOptions(options: PopupCommonOptions) {this.options = options;}public setIsPartialUpdate(isPartialUpdate: boolean) {this.isPartialUpdate = isPartialUpdate;}public openPopup() {if (this.promptAction != null) {this.promptAction.openPopup(this.contentNode, this.target, this.options).then(() => {console.info('openPopup success');}).catch((err: BusinessError) => {console.error('openPopup error: ' + err.code + ' ' + err.message);})}}public closePopup() {if (this.promptAction != null) {this.promptAction.closePopup(this.contentNode).then(() => {console.info('closePopup success');}).catch((err: BusinessError) => {console.error('closePopup error: ' + err.code + ' ' + err.message);})}}public updatePopup(options: PopupCommonOptions) {if (this.promptAction != null) {this.promptAction.updatePopup(this.contentNode, options, this.isPartialUpdate).then(() => {console.info('updatePopup success');}).catch((err: BusinessError) => {console.error('updatePopup error: ' + err.code + ' ' + err.message);})}}
}
// entry/src/main/ets/pages/Index.etsimport { PromptActionClass } from "library";
import { ComponentContent, PromptAction } from '@kit.ArkUI';class Params {text: string = "";promptActionClass: PromptActionClass = new PromptActionClass();constructor(text: string, promptActionClass: PromptActionClass) {this.text = text;this.promptActionClass = promptActionClass;}
}@Builder
function buildText(params: Params) {Column() {Text(params.text).fontSize(20).margin({ top: 10 })Button('Update').margin({ top: 10 }).width(100).onClick(() => {params.promptActionClass.updatePopup({enableArrow: false,});})Button('Close').margin({ top: 10 }).width(100).onClick(() => {params.promptActionClass.closePopup();})}.width(130).height(150)
}@Entry
@Component
struct Index {@State message: string = "hello";private uiContext: UIContext = this.getUIContext();private promptAction: PromptAction = this.uiContext.getPromptAction();private promptActionClass: PromptActionClass = new PromptActionClass();private targetId: number = 0;private contentNode: ComponentContent<Object> =new ComponentContent(this.uiContext, wrapBuilder(buildText), new Params(this.message, this.promptActionClass));private options: PopupCommonOptions = { enableArrow: true };build() {Column() {Button("openPopup").margin({ top: 50, left: 100 }).onClick(() => {let frameNode: FrameNode | null = this.uiContext.getFrameNodeByUniqueId(this.getUniqueId());let targetId = frameNode?.getChild(0)?.getUniqueId();if (targetId == undefined) {this.targetId = 0;} else {this.targetId = targetId;}this.promptActionClass.setPromptAction(this.promptAction);this.promptActionClass.setContentNode(this.contentNode);this.promptActionClass.setOptions(this.options);this.promptActionClass.setIsPartialUpdate(false);this.promptActionClass.setTarget({ id: this.targetId });this.promptActionClass.openPopup();})}}
}

 

不依赖UI组件的全局菜单 (openMenu)

菜单控制 (Menu)在使用时依赖绑定UI组件,否则无法使用。从API version 18开始,可以通过使用全局接口openMenu的方式,在无UI组件的场景下直接或封装使用,例如在事件回调中使用或封装后对外提供能力。

弹出菜单

通过openMenu可以弹出菜单。

promptAction.openMenu(contentNode, { id: targetId }, {enableArrow: true
}).then(() => {console.info('openMenu success');}).catch((err: BusinessError) => {console.error('openMenu error: ' + err.code + ' ' + err.message);})

创建ComponentContent

通过调用openMenu接口弹出菜单,需要提供用于定义自定义弹出框的内容ComponentContent。其中,wrapBuilder(buildText)封装自定义组件,new Params(this.message)是自定义组件的入参,可以缺省,也可以传入基础数据类型。

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

如果在wrapBuilder中包含其他组件(例如:Popup、Chip组件),则ComponentContent应采用带有四个参数的构造函数constructor,其中options参数应传递{ nestingBuilderSupported: true }。

@Builder
export function buildText(params: Params) {Menu({// 类型设置图标内容icon: {image: $r('app.media.app_icon'),width: 32,height: 32,fillColor: Color.White,borderRadius: 10} as MenuIconOptions,// 设置文字内容title: {text: `This is a Menu title 1`,fontSize: 20,fontColor: Color.Black,fontWeight: FontWeight.Normal} as MenuTextOptions,// 设置文字内容message: {text: `This is a Menu message 1`,fontSize: 15,fontColor: Color.Black} as MenuTextOptions,// 设置按钮内容buttons: [{text: 'confirm',action: () => {console.info('confirm button click');},fontSize: 15,fontColor: Color.Black,},{text: 'cancel',action: () => {console.info('cancel button click');},fontSize: 15,fontColor: Color.Black},] as [MenuButtonOptions?, MenuButtonOptions?]})
}let contentNode: ComponentContent<Object> = new ComponentContent(uiContext, wrapBuilder(buildText), this.message, { nestingBuilderSupported: true });

绑定组件信息

通过调用openMenu接口弹出菜单,需要提供绑定组件的信息TargetInfo。若未传入有效的target,则无法弹出菜单。

let frameNode: FrameNode | null = this.ctx.getFrameNodeByUniqueId(this.getUniqueId());
let targetId = frameNode?.getChild(0)?.getUniqueId();

设置弹出菜单样式

通过调用openMenu接口弹出菜单,可以设置MenuOptions属性调整菜单样式。title属性不生效。preview参数仅支持设置MenuPreviewMode类型。

private options: MenuOptions = { enableArrow: true, placement: Placement.Bottom };

更新菜单样式

通过updateMenu可以更新菜单的样式。支持全量更新和增量更新其菜单样式,不支持更新showInSubWindow、preview、previewAnimationOptions、transition、onAppear、aboutToAppear、onDisappear和aboutToDisappear。

promptAction.updateMenu(contentNode, {enableArrow: false
}, true).then(() => {console.info('updateMenu success');}).catch((err: BusinessError) => {console.error('updateMenu error: ' + err.code + ' ' + err.message);})

关闭菜单

通过调用closeMenu可以关闭菜单。

promptAction.closeMenu(contentNode).then(() => {console.info('openMenu success');}).catch((err: BusinessError) => {console.error('openMenu error: ' + err.code + ' ' + err.message);})

由于updateMenu和closeMenu依赖content来更新或者关闭指定的菜单,开发者需自行维护传入的content。

在HAR包中使用全局菜单

可以通过HAR包封装一个Menu,从而对外提供菜单的弹出、更新和关闭能力。

Toast

即时反馈(Toast)是一种临时性的消息提示框,用于向用户显示简短的操作反馈或状态信息。​它通常在屏幕的底部或顶部短暂弹出,随后在一段时间后自动消失。即时反馈的主要目的是提供简洁、不打扰的信息反馈,避免干扰用户当前的操作流程。

可以通过使用UIContext中的getPromptAction方法获取当前UI上下文关联的PromptAction对象,再通过该对象调用showToast创建并显示文本提示框。

为了安全考虑,例如Toast恶意遮挡其他页面,Toast只能显示在当前的UI实例中,应用退出后,不会单独显示在桌面上。

使用建议

  • 合理使用弹出场景,避免过度提醒用户。
  • 可以针对以下常用场景使用即时反馈操作,例如,当用户执行某个操作时及时结果反馈,用来提示用户操作是否成功或失败;或是当应用程序的状态发生变化时提供状态更新等。
  • 注意文本的信息密度,即时反馈展示时间有限,应当避免长文本的出现。
  • Toast控件的文本应该清晰可读,字体大小和颜色应该与应用程序的主题相符。除此之外,即时反馈控件本身不应该包含任何可交互的元素,如按钮或链接。
  • 杜绝强制占位和密集弹出的提示。
  • 即时反馈作为应用内的轻量通知,应当避免内容布局占用界面内的其他元素信息,如遮盖弹出框的展示内容,从而迷惑用户弹出的内容是否属于弹出框。再或者频繁性的弹出信息内容,且每次弹出之间无时间间隔,影响用户的正常使用。也不要在短时间内频繁弹出新的即时反馈替代上一个。即时反馈的单次显示时长不要超过 3 秒钟,避免影响用户正常的行为操作。
  • 遵从系统默认弹出位置。
  • 即时反馈在系统中默认从界面底部弹出,距离底部有一定的安全间距,作为系统性的应用内提示反馈,请遵守系统默认效果,避免与其他弹出类组件内容重叠。特殊场景下可对内容布局进行规避。

即时反馈模式对比

即时反馈提供了两种显示模式,分别为DEFAULT(显示在应用内)、TOP_MOST(显示在应用之上)。

在TOP_MOST类型的Toast显示前,会创建一个全屏大小的子窗(手机上子窗大小和主窗大小一致),然后在该子窗上计算Toast的布局位置,最后显示在该子窗上。具体和DEFAULT模式Toast的差异如下:

差异点DEFAULTTOP_MOST
是否创建子窗
层级显示在主窗内,层级和主窗一致,一般比较低显示在子窗中,一般比主窗层级高,比其他弹窗类组件层级高,比软键盘和权限弹窗层级低
是否避让软键盘软键盘抬起时,必定上移软键盘的高度软键盘抬起时,只有toast被遮挡时,才会避让,且避让后toast底部距离软键盘高度为80vp
UIExtension内布局以UIExtension为主窗中布局,对齐方式与UIExtension对齐以宿主窗口为主窗中布局,对齐方式与宿主窗口对齐
import { promptAction } from '@kit.ArkUI';@Entry
@Component
struct Index {build() {Column({ space: 10 }) {TextInput()Button() {Text("DEFAULT类型Toast").fontSize(20).fontWeight(FontWeight.Bold)}.width('100%').onClick(() => {this.getUIContext().getPromptAction().showToast({message: "ok,我是DEFAULT toast",duration: 2000,showMode: promptAction.ToastShowMode.DEFAULT,bottom: 80});})Button() {Text("TOPMOST类型Toast").fontSize(20).fontWeight(FontWeight.Bold)}.width('100%').onClick(() => {this.getUIContext().getPromptAction().showToast({message: "ok,我是TOP_MOST toast",duration: 2000,showMode: promptAction.ToastShowMode.TOP_MOST,bottom: 85});})}}
}

创建即时反馈

适用于短时间内提示框自动消失的场景

import { PromptAction } from '@kit.ArkUI';
import { BusinessError } from '@kit.BasicServicesKit';@Entry
@Component
struct toastExample {private uiContext: UIContext = this.getUIContext();private promptAction: PromptAction = this.uiContext.getPromptAction();build() {Column() {Button('Show toast').fontSize(20).onClick(() => {try {this.promptAction.showToast({message: 'Hello World',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}`);};})}.height('100%').width('100%').justifyContent(FlexAlign.Center)}
}

显示关闭即时反馈

适用于提示框提留时间较长,用户操作可以提前关闭提示框的场景。

import { LengthMetrics, PromptAction } from '@kit.ArkUI';
import { BusinessError } from '@kit.BasicServicesKit';@Entry
@Component
struct toastExample {@State toastId: number = 0;private uiContext: UIContext = this.getUIContext();private promptAction: PromptAction = this.uiContext.getPromptAction();build() {Column() {Button('Open Toast').type(ButtonType.Capsule).height(100).onClick(() => {try {this.promptAction.openToast({message: 'Toast Massage',duration: 10000,}).then((toastId: number) => {this.toastId = toastId;});} catch (error) {let message = (error as BusinessError).message;let code = (error as BusinessError).code;console.error(`OpenToast error code is ${code}, message is ${message}`);};})Blank().height(50);Button('Close Toast').height(100).type(ButtonType.Capsule).onClick(() => {try {this.promptAction.closeToast(this.toastId);} catch (error) {let message = (error as BusinessError).message;let code = (error as BusinessError).code;console.error(`CloseToast error code is ${code}, message is ${message}`);};})}.height('100%').width('100%').justifyContent(FlexAlign.Center)}
}

http://www.lryc.cn/news/592276.html

相关文章:

  • ModbusRTU转profibus网关与涡街液体流量计通讯读取流量计温度
  • 回归预测 | MATLAB实现SA-BP模拟退火算法优化BP神经网络多输入单输出回归预测
  • jvm分析篇---1、先认识下dump文件
  • Kubernetes Pod 深度理解
  • 【C语言进阶】题目练习(2)
  • Composer 可以通过指定 PHP 版本运行
  • uni-app 跳转外部连接
  • 网络原理——UDP
  • 如何 ASP.NET Core 中使用 WebSocket
  • html复习
  • 【收集电脑信息】collect_info.sh
  • 电脑插上u盘不显示怎么回事
  • 分表聚合助手类
  • 分布式面试点
  • RecyclerView与ListView深度对比分析
  • 从复合变量到分组分析:piecewiseSEM 解析生态系统多因子交互作用
  • UDP 协议下一发一收通信程序的实现与解析
  • Linux 定时任务全解析:atd 与 crond 的区别及实战案例(含日志备份 + 时间写入)
  • AugmentCode还没对个人开放?
  • 双通达信多板块同步
  • 如何抉择HTTPSProxy?
  • AR技术重塑电力巡检:效率提升与智能升级
  • 三维重建一: 相机几何
  • 基于ROS2进行相机标定,并通过测试相机到棋盘格之间的距离进行验证
  • Go语言实战案例-斐波那契数列生成器
  • 9. isaacsim4.2教程-ROS加相机/CLOCK
  • pgsql导出导入超过50G数据、解决方法
  • hive分区表临时加载日批数据文件
  • MyBatis 动态 SQL:让 SQL 语句随条件灵活变化
  • [源力觉醒 创作者计划]_文心一言 4.5开源深度解析:性能狂飙 + 中文专精