【HarmonyOS】封装可以同时显示多个toast的工具类
src/main/ets/common/MyPromptActionUtil.ets
import { ComponentContent, PromptAction, window } from '@kit.ArkUI';
import { BusinessError } from '@kit.BasicServicesKit';// MyPromptInfo 类用于生成唯一的 dialogID
export class MyPromptInfo {public dialogID: stringconstructor() {this.dialogID = this.generateRandomString(10)}// 生成指定长度的随机字符串generateRandomString(length: number): string {const characters = 'abcdefghijklmnopqrstuvwxyz0123456789';let result = '';for (let i = 0; i < length; i++) {const randomIndex = Math.floor(Math.random() * characters.length);result += characters.charAt(randomIndex);}return result;}
}// MyPromptActionUtil 类用于封装弹窗操作
export class MyPromptActionUtil<T extends MyPromptInfo> {static myDialogPromptActionUtil: MyPromptActionUtil<MyToastInfo> | undefined = undefinedpublic static showToast(message: string) {if (!MyPromptActionUtil.myDialogPromptActionUtil) { //当前页面没显示toast// getContext().eventHub.off(MyPromptActionUtil.myDialogPromptActionUtil?.dialogID)// MyPromptActionUtil.myDialogPromptActionUtil?.closeCustomDialog() //如果之前有的toast对话框,并且正在显示,则先关闭toast提示window.getLastWindow(getContext()).then((windowClass) => {const uiContext = windowClass.getUIContext()MyPromptActionUtil.myDialogPromptActionUtil =new MyPromptActionUtil<MyToastInfo>(uiContext, wrapBuilder(myToastView), new MyToastInfo(message)).setModal(false)//true:存在黑色半透明蒙层,false:没有蒙层.setSwipeBackEnabled(false)//true:侧滑允许关闭弹窗.setMaskTapToCloseEnabled(true)//true:点击半透明蒙层可关闭弹窗【注:如果setModal(false),那么就没有蒙层,所以点击对话框外也没有响应事件,也就是这里设置了也没效果,并且事件会穿透】.setAlignment(DialogAlignment.Center).onWillAppear(() => {console.info('在对话框的打开动画开始之前调用的回调函数')getContext().eventHub.on(MyPromptActionUtil.myDialogPromptActionUtil?.dialogID, (data: string) => {//监听结果if (data == '关闭弹窗') {MyPromptActionUtil.myDialogPromptActionUtil?.closeCustomDialog()}})}).onWillDisappear(() => {console.info('在对话框的关闭动画开始之前调用的回调函数')getContext().eventHub.off(MyPromptActionUtil.myDialogPromptActionUtil?.dialogID)MyPromptActionUtil.myDialogPromptActionUtil = undefined}).showCustomDialog()})} else { //当前正在显示toastgetContext().eventHub.emit(MyPromptActionUtil.myDialogPromptActionUtil.dialogID, { msg: message })}}private uiContext: UIContext;private promptAction: PromptAction;private contentNode: ComponentContent<T> | undefined;private wrapBuilder: WrappedBuilder<[T]>;private t: T;private isModal: boolean = true;private alignment: DialogAlignment = DialogAlignment.Center;private isSwipeBackEnabled: boolean = true;private isMaskTapToCloseEnabled: boolean = true;public dialogID: stringconstructor(uiContext: UIContext, wrapBuilder: WrappedBuilder<[T]>, t: T) {this.uiContext = uiContext;this.promptAction = uiContext.getPromptAction();this.wrapBuilder = wrapBuilder;this.t = t;this.dialogID = t.dialogID}setSwipeBackEnabled(isSwipeBackEnabled: boolean) {this.isSwipeBackEnabled = isSwipeBackEnabled;return this;}setMaskTapToCloseEnabled(isMaskTapToCloseEnabled: boolean) {this.isMaskTapToCloseEnabled = isMaskTapToCloseEnabledreturn this;}setAlignment(alignment: DialogAlignment) {this.alignment = alignment;return this;}setModal(isModal: boolean) {this.isModal = isModal;return this;}onDidAppear(callback: () => void) {this.onDidAppearCallback = callback;return this;}onDidDisappear(callback: () => void) {this.onDidDisappearCallback = callback;return this;}onWillAppear(callback: () => void) {this.onWillAppearCallback = callback;return this;}onWillDisappear(callback: () => void) {this.onWillDisappearCallback = callback;return this;}private onDidAppearCallback?: () => void;private onDidDisappearCallback?: () => void;private onWillAppearCallback?: () => void;private onWillDisappearCallback?: () => void;closeCustomDialog() {if (this.contentNode) {this.promptAction.closeCustomDialog(this.contentNode);}return this;}// 显示自定义弹窗showCustomDialog() {try {if (!this.contentNode) {this.contentNode = new ComponentContent(this.uiContext, this.wrapBuilder, this.t);}this.promptAction.openCustomDialog(this.contentNode, {// 打开自定义弹窗alignment: this.alignment,isModal: this.isModal,showInSubWindow: false,maskRect: {x: 0,y: 0,width: '100%',height: '100%'},onWillDismiss: (dismissDialogAction: DismissDialogAction) => { //弹窗响应console.info("reason" + JSON.stringify(dismissDialogAction.reason))console.log("dialog onWillDismiss")if (dismissDialogAction.reason == 0 && this.isSwipeBackEnabled) { //手势返回时,关闭弹窗。this.promptAction.closeCustomDialog(this.contentNode)}if (dismissDialogAction.reason == 1 && this.isMaskTapToCloseEnabled) {this.promptAction.closeCustomDialog(this.contentNode)}},onDidAppear: this.onDidAppearCallback ? this.onDidAppearCallback : () => {},onDidDisappear: this.onDidDisappearCallback ? this.onDidDisappearCallback : () => {},onWillAppear: this.onWillAppearCallback ? this.onWillAppearCallback : () => {},onWillDisappear: this.onWillDisappearCallback ? this.onWillDisappearCallback : () => {},});} catch (error) { // 错误处理let message = (error as BusinessError).message;let code = (error as BusinessError).code;console.error(`OpenCustomDialog args error code is ${code}, message is ${message}`);}return this;}
}class MyToastInfo extends MyPromptInfo {public message: string = ""constructor(message: string) {super()this.message = message}
}@Builder
function myToastView(data: MyToastInfo) {MyToastView({ dialogID: data.dialogID, message: data.message })
}@ObservedV2
class ToastBean {message: string = ""@Trace isShow: boolean = trueconstructor(message: string) {this.message = message}
}@Component
struct MyToastView {@State toast_info_list: ToastBean[] = []@Prop dialogID: string@Prop message: stringaboutToAppear(): void {this.toast_info_list.push(new ToastBean(this.message))getContext().eventHub.on(this.dialogID, (data: object) => {if (data['msg']) {this.toast_info_list.push(new ToastBean(data['msg']))}})}build() {Column() {ForEach(this.toast_info_list, (item: ToastBean) => {Text(item.message).fontSize('36lpx').fontColor(Color.White).backgroundColor("#B2ff0000").borderRadius(8).constraintSize({ maxWidth: '80%' }).padding({bottom: '28lpx',left: '60lpx',right: '60lpx',top: '28lpx'}).margin(5).visibility(item.isShow ? Visibility.Visible : Visibility.None).onVisibleAreaChange([0.0, 1.0], (isVisible: boolean, currentRatio: number) => {console.info('Test Text isVisible: ' + isVisible + ', currentRatio:' + currentRatio)if (isVisible && currentRatio >= 1.0) {setTimeout(() => {item.isShow = false}, 2000)}}).animation({duration: 200, onFinish: () => {console.info('==== onFinish')//动画结束后,判断数组是否已全部为隐藏状态,是的话证明所有toast内容都展示完成,可以释放全局弹窗了let isAnimAll = truefor (let i = 0; i < this.toast_info_list.length; i++) {if (this.toast_info_list[i].isShow == true) { //至少有一个正在显示isAnimAll = falsebreak;}}if (isAnimAll) {console.info('已展示完全部toast,为了性能,关闭弹窗释放view')getContext(this).eventHub.emit(this.dialogID, "关闭弹窗")}}}).transition(TransitionEffect.OPACITY.animation({ duration: 200 }))})}}
}
src/main/ets/pages/Page01.ets
import { MyPromptActionUtil } from '../common/MyPromptActionUtil'@Entry
@Component
struct Page01 {build() {Column() {Button('显示Toast').onClick(() => {MyPromptActionUtil.showToast(`随机数:${this.getRandomInt(1, 100)}`)})}.width('100%').height('100%')}getRandomInt(min: number, max: number): number {min = Math.ceil(min);max = Math.floor(max);return Math.floor(Math.random() * (max - min + 1)) + min;}
}