封装可以同时显示多个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: string

  constructor() {
    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 = undefined

  public 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 { //当前正在显示toast
      getContext().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: string

  constructor(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 = isMaskTapToCloseEnabled
    return 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 = true

  constructor(message: string) {
    this.message = message
  }
}

@Component
struct MyToastView {
  @State toast_info_list: ToastBean[] = []
  @Prop dialogID: string
  @Prop message: string

  aboutToAppear(): 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 = true
              for (let i = 0; i < this.toast_info_list.length; i++) {
                if (this.toast_info_list[i].isShow == true) { //至少有一个正在显示
                  isAnimAll = false
                  break;
                }
              }
              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;
  }
}

zhongcx
1 声望3 粉丝