目录
不依赖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的差异如下:
差异点 | DEFAULT | TOP_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)}
}