ToB企服应用市场:ToB评测及商务社交产业平台

标题: HarmonyOS鸿蒙开辟 弹窗及加载中指示器HUD功能实现 [打印本页]

作者: 天空闲话    时间: 2025-1-11 20:48
标题: HarmonyOS鸿蒙开辟 弹窗及加载中指示器HUD功能实现
HarmonyOS鸿蒙开辟 弹窗及加载中指示器HUD功能实现
最近在学习鸿蒙开辟过程中,阅读了官方文档,在之前做flutter时候,经常使用overlay,使用OverlayEntry加入到overlayState来做添加悬浮按钮、提示弹窗、加载中指示器、加载失败的toast等功能。那在HarmonyOS鸿蒙开辟中也可能有类似的功能需求。
HarmonyOS鸿蒙开辟的使用弹窗文档中已经非常详细了
地点:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/arkts-use-dialog-V5
一、子窗口window

在弹出的loading指示器中,我们可以使用创建子window的方式,调用window的loadContentByName方法来实现。
实现步调
效果预览


在LoadingHud中有LoadingProgress、Text提示文本,Text显示的信息由LocalStorage进行传递
必要传递的数据message,在aboutToAppear进行赋值
  1. @Local message: string = '';
  2.   aboutToAppear(): void {
  3.     this.message = LocalStorage.getShared().get("message") ?? "";
  4.   }
复制代码
固然在调用window的loadContentByName时候,必要确定加载的主角的routeName,这就必要在LoadingHud组件中使用装饰器来设置
  1. /// 通用的hud,弹出框,或者loading框
  2. @Entry({ routeName: "hudLoading", storage: LocalStorage.getShared() })
  3. @ComponentV2
  4. export struct LoadingHud {
  5.   ... 其他代码
  6. }
复制代码
LoadingHud的完备代码如下:
  1. /// 通用的hud,弹出框,大概loading框@Entry({ routeName: "hudLoading", storage: LocalStorage.getShared() })@ComponentV2export struct LoadingHud {  @Local message: string = '';
  2.   aboutToAppear(): void {
  3.     this.message = LocalStorage.getShared().get("message") ?? "";
  4.   }
  5.   build() {    Column() {      Column(){        Row() {          // 从左往右,1号环形进度条,默认前景致为蓝色渐变,默认strokeWidth进度条宽度为2.0vp          LoadingProgress()            .color($r('app.color.success'))            .width(40)            .height(40)          // message          Text(this.message)            .fontSize(14)            .fontColor($r('app.color.dataset_empty_message'))            .margin({              left: 10            })        }        .padding({          top: 15,          bottom: 15,          left: 15,          right: 20        })        .justifyContent(FlexAlign.Center)        Button("点击消散")          .width(100)          .height(40)          .fontSize(12)          .backgroundColor('#ef04792c')          .margin({            top: 10          })          .onClick(()=> {             LoadingHudUtil.dismissLoading();          })      }      .justifyContent(FlexAlign.Center)      .constraintSize({        minWidth: 200,        minHeight: 150,      })      .backgroundColor($r('app.color.white'))      .borderRadius(10)    }    .justifyContent(FlexAlign.Center)    .width('100%')    .height('100%')    .backgroundColor('#00000000')    .hitTestBehavior(HitTestMode.Transparent)  }}
复制代码

在创建LoadingHud后,我们必要创建创建子Window并显示window,显示我们的loadingHUD
创建window的createWindow,这里使用的windowType是window.WindowType.TYPE_DIALOG,也可以换成其他的试试看。
  1. let windowName = "loading";
  2.       // 创建窗口
  3.       let subWindow = await window.createWindow(
  4.         {
  5.           name: windowName,
  6.           windowType: window.WindowType.TYPE_DIALOG,
  7.           ctx: ctx,
  8.         }
  9.       );
复制代码
设置LocalStorage数据,存储message
  1. //创建存储
  2.       let storage = new LocalStorage();
  3.       //存储数据
  4.       storage.setOrCreate('message',  tip);
复制代码
调用window的loadContentByName,设置Window的大小及背景颜色,显示Window
  1. await subWindow.loadContentByName('hudLoading', storage);
  2.       let dp = display.getDefaultDisplaySync();
  3.       await subWindow.resize(dp.width, dp.height);
  4.       subWindow.setWindowBackgroundColor('#30000000');
  5.       await subWindow.showWindow();
复制代码
显示后Window,在必要消散的时候调用destroyWindow
  1. static async dismissLoading(): Promise<void> {
  2.     if (LoadingHudUtil.cacheWindow) {
  3.       await LoadingHudUtil.cacheWindow.destroyWindow();
  4.     }
  5.   }
复制代码
完备的LoadingHudUtil的代码如下
  1. import { display, window } from '@kit.ArkUI';import { common } from '@kit.AbilityKit';import('../components/hud/LoadingHud'); // 引入命名路由页面// 自定义弹出窗口export class LoadingHudUtil {  private static cacheWindow: window.Window;  static async showLoading(tip: string): Promise<void> {    let ctx = getContext() as common.UIAbilityContext;    try {      let windowName = "loading";
  2.       // 创建窗口
  3.       let subWindow = await window.createWindow(
  4.         {
  5.           name: windowName,
  6.           windowType: window.WindowType.TYPE_DIALOG,
  7.           ctx: ctx,
  8.         }
  9.       );
  10.       LoadingHudUtil.cacheWindow = subWindow;      //创建存储
  11.       let storage = new LocalStorage();
  12.       //存储数据
  13.       storage.setOrCreate('message',  tip);
  14.       console.log("LoadingHudUtil loadContentByName" + tip);      // subWindow.setGestureBackEnabled(false);      // subWindow.setDialogBackGestureEnabled(false);      // subWindow.setWindowTouchable(true);      await subWindow.loadContentByName('hudLoading', storage);
  15.       let dp = display.getDefaultDisplaySync();
  16.       await subWindow.resize(dp.width, dp.height);
  17.       subWindow.setWindowBackgroundColor('#30000000');
  18.       await subWindow.showWindow();
  19.     } catch (e) {      console.log("LoadingHudUtil showLoading e:" + JSON.stringify(e));    }  }  static async dismissLoading(): Promise<void> {
  20.     if (LoadingHudUtil.cacheWindow) {
  21.       await LoadingHudUtil.cacheWindow.destroyWindow();
  22.     }
  23.   }
  24. }
复制代码
二、自定义Dialog

在HarmonyOS鸿蒙开辟中,可以使用CustomDialogController来实现自定义的弹窗。
效果预览


在CustomAlertDialog中实现一个消息提示,并且点击按钮可以关闭dialog
代码如下:
  1. @CustomDialog
  2. export struct CustomAlertDialog {
  3.   controller?: CustomDialogController
  4.   title?: string
  5.   build() {
  6.     Column() {
  7.       Column() {
  8.         // message
  9.         Text(this.title)
  10.           .fontSize(14)
  11.           .fontColor($r('app.color.dataset_empty_message'))
  12.           .margin({
  13.             left: 10
  14.           })
  15.         Button("点击消失")
  16.           .width(100)
  17.           .height(40)
  18.           .fontSize(12)
  19.           .backgroundColor('#ef04792c')
  20.           .margin({
  21.             top: 10
  22.           })
  23.           .onClick(() => {
  24.             this.controller?.close();
  25.           })
  26.       }
  27.       .justifyContent(FlexAlign.Center)
  28.       .constraintSize({
  29.         minWidth: 200,
  30.         minHeight: 100,
  31.       })
  32.       .backgroundColor($r('app.color.white'))
  33.       .borderRadius(10)
  34.     }
  35.     .justifyContent(FlexAlign.Center)
  36.     .width('100%')
  37.     .height('100%')
  38.     .backgroundColor(Color.Transparent)
  39.     .hitTestBehavior(HitTestMode.Transparent)
  40.   }
  41. }
复制代码

定义CustomDialogController
  1. // 自定义CustomDialog
  2.   customDialogController: CustomDialogController | null = new CustomDialogController({
  3.     builder: CustomAlertDialog({
  4.       title: "温馨提示"
  5.     }),
  6.     alignment: DialogAlignment.Center,
  7.     onWillDismiss: (dismissDialogAction: DismissDialogAction) => {
  8.       console.info("reason=" + JSON.stringify(dismissDialogAction.reason))
  9.       console.log("dialog onWillDismiss")
  10.       if (dismissDialogAction.reason == DismissReason.PRESS_BACK) {
  11.         dismissDialogAction.dismiss()
  12.       }
  13.       if (dismissDialogAction.reason == DismissReason.TOUCH_OUTSIDE) {
  14.         dismissDialogAction.dismiss()
  15.       }
  16.     },
  17.     autoCancel: true,
  18.     customStyle: true,
  19.   });
复制代码
在必要展示弹窗的时候调用customDialogController的open方法。
  1. if (this.customDialogController != null) {
  2.                       this.customDialogController.open()
  3.                     }
复制代码
固然假如页面消散,只管在aboutToDisappear中将customDialogController置空
  1.   // 在自定义组件即将销毁时将dialogController置空
  2.   aboutToDisappear() {
  3.     this.customDialogController = null // 将dialogController置空
  4.   }
复制代码
三、Overlay浮层

在官方文档中有一段描述
浮层(OverlayManager) 用于将自定义的UI内容展示在页面(Page)之上,在Dialog、Popup、Menu、BindSheet、BindContentCover和Toast等组件之下,展示的范围为当前窗口安全区内。可适用于常驻悬浮等场景。
使用OverlayManager来添加、删除、隐藏、显示节点Component
效果预览




在CustomOverlayView中,我们定义了加载中,加载失败,加载乐成的几种范例,用于展示不同的样式
定义OverlayConfig类为展示的界面配置、OverlayScaleImage缩放的icon
  1. export enum OverlayType {
  2.   loading,
  3.   success,
  4.   fail
  5. }
  6. export class OverlayConfig {
  7.   message: string = ""
  8.   offset: Position = { x: 0, y: -50 }
  9.   index: number = 0
  10.   autoDismiss: boolean = true;
  11.   duration: number = 3000 // 持续时间
  12.   onCallback?: (index: number) => void
  13.   type: OverlayType = OverlayType.loading
  14.   constructor(message: string) {
  15.     this.message = message
  16.   }
  17. }
  18. @Builder
  19. export function builderCustomOverlayView(overlayConfig: OverlayConfig) {
  20.   CustomOverlayView({
  21.     olConfig: overlayConfig
  22.   })
  23. }
  24. @ComponentV2
  25. struct OverlayScaleImage {
  26.   @Param @Require src: PixelMap | ResourceStr | DrawableDescriptor;
  27.   @Param imgWidth: number = 40;
  28.   @Local imgScale: number = 0.0;
  29.   build() {
  30.     Image(this.src)
  31.       .width(this.imgWidth)
  32.       .aspectRatio(1)
  33.       .scale({ x: this.imgScale, y: this.imgScale })
  34.       .animation({
  35.         duration: 300, // 时长
  36.         iterations: 1, // 设置-1表示动画无限循环
  37.       })
  38.       .onAppear(() => {
  39.         // 组件挂载完毕,修改数值触发动画效果
  40.         this.imgScale = 1.0
  41.       })
  42.   }
  43. }
  44. @ComponentV2
  45. export struct CustomOverlayView {
  46.   @Param olConfig: OverlayConfig = new OverlayConfig("");
  47.   aboutToAppear(): void {
  48.     setTimeout(() => {
  49.       console.log("CustomOverlayView aboutToAppear");
  50.       if (this.olConfig.onCallback != null) {
  51.         this.olConfig.onCallback(this.olConfig.index);
  52.       }
  53.     }, this.olConfig.duration);
  54.   }
  55.   build() {
  56.     Column() {
  57.       if (OverlayType.loading == this.olConfig.type) {
  58.         // message
  59.         LoadingProgress()
  60.           .color($r('app.color.success'))
  61.           .width(40)
  62.           .height(40)
  63.       } else if (OverlayType.success == this.olConfig.type) {
  64.         // message
  65.         OverlayScaleImage({
  66.           src: $r('app.media.ic_hud_success'),
  67.           imgWidth: 40
  68.         })
  69.       } else if (OverlayType.fail == this.olConfig.type) {
  70.         // message
  71.         OverlayScaleImage({
  72.           src: $r('app.media.ic_hud_fail'),
  73.           imgWidth: 30
  74.         })
  75.       }
  76.       Text(this.olConfig.message)
  77.         .fontSize(14)
  78.         .fontColor($r('app.color.white'))
  79.         .margin({
  80.           top: 10,
  81.         })
  82.     }
  83.     .padding({
  84.       top: 20,
  85.       bottom: 20,
  86.       left: 15,
  87.       right: 15
  88.     })
  89.     .justifyContent(FlexAlign.Center)
  90.     .constraintSize({
  91.       minWidth: 180,
  92.       minHeight: 80,
  93.     })
  94.     .backgroundColor($r('app.color.overlay_bg_color'))
  95.     .borderRadius(10)
  96.     .offset(this.olConfig.offset)
  97.   }
  98. }
复制代码

由于在OverlayManager来添加、删除、隐藏、显示节点过程中,必要使用index索引参数。这里使用一个类,类中有一个数组记录一下展示的节点Component
  1. @ObservedV2
  2. export class CustomOverlayStorage {
  3.   @Trace contentArray: ComponentContent<OverlayConfig>[] = []
  4. }
复制代码

起首确定属性uiContext,创建ComponentContent必要该参数,这个我在index.ets中进行初始化传入。
CustomOverlayStorage存储ComponentContent的数组,确定index
overlayManager用来来添加、删除、隐藏、显示节点
MyOverlayManager代码如下
  1. /// 用于管理Overlay
  2. /// 浮层(OverlayManager) 用于将自定义的UI内容展示在页面(Page)之上,
  3. /// 在Dialog、Popup、Menu、BindSheet、BindContentCover和Toast等组件之下,
  4. /// 展示的范围为当前窗口安全区内。可适用于常驻悬浮等场景。
  5. /// 与OverlayManager相关的属性推荐采用AppStorage来进行应用全局存储,以免切换页面后属性值发生变化从而导致业务错误。
  6. import { AppStorageV2, ComponentContent, OverlayManager, router } from '@kit.ArkUI';
  7. import {
  8.   builderCustomOverlayView,
  9.   CustomOverlayStorage,
  10.   OverlayConfig
  11. } from '../common/components/hud/CustomOverlayView';
  12. export class MyOverlayManager {
  13.   private static currentIndex: number = 0;
  14.   private uiContext?: UIContext
  15.   private overlayManager?: OverlayManager
  16.   private overlayStorage: CustomOverlayStorage =
  17.     AppStorageV2.connect(CustomOverlayStorage, 'overlayStorage', () => new CustomOverlayStorage())!;
  18.   private static instance: MyOverlayManager;
  19.   public static getInstance(): MyOverlayManager {
  20.     if (MyOverlayManager.instance == null) {
  21.       MyOverlayManager.instance = new MyOverlayManager();
  22.     }
  23.     return MyOverlayManager.instance;
  24.   }
  25.   initOverlayNode(uiContext: UIContext): void {
  26.     this.uiContext = uiContext;
  27.     this.overlayManager = uiContext.getOverlayManager();
  28.   }
  29.   addOverlayView(overlayConfig: OverlayConfig): void {
  30.     if (this.uiContext != null && this.uiContext != undefined) {
  31.       // 设置索引下标
  32.       let index = MyOverlayManager.currentIndex++;
  33.       overlayConfig.index = index;
  34.       // 创建componentContent
  35.       let componentContent = new ComponentContent(
  36.         this.uiContext!, wrapBuilder<[OverlayConfig]>(builderCustomOverlayView),
  37.         overlayConfig
  38.       )
  39.       this.overlayStorage.contentArray.push(componentContent);
  40.       if (this.overlayManager != null && this.overlayManager != undefined) {
  41.         this.overlayManager.addComponentContent(componentContent, index)
  42.       }
  43.     }
  44.   }
  45.   hideOverlayView(index: number) {
  46.     if (this.overlayManager != null && this.overlayManager != undefined) {
  47.       if (index < this.overlayStorage.contentArray.length) {
  48.         this.overlayManager.hideComponentContent(this.overlayStorage.contentArray[index])
  49.       }
  50.     }
  51.   }
  52.   showOverlayView(index: number) {
  53.     if (this.overlayManager != null && this.overlayManager != undefined) {
  54.       if (index < this.overlayStorage.contentArray.length) {
  55.         this.overlayManager.showComponentContent(this.overlayStorage.contentArray[index])
  56.       }
  57.     }
  58.   }
  59.   removeOverlayView(index: number) {
  60.     if (this.overlayManager != null && this.overlayManager != undefined) {
  61.       if (index < this.overlayStorage.contentArray.length) {
  62.         this.overlayManager.removeComponentContent(this.overlayStorage.contentArray[index])
  63.       }
  64.     }
  65.   }
  66.   removeAllOverlayView() {
  67.     if (this.overlayManager != null && this.overlayManager != undefined) {
  68.       this.overlayManager.hideAllComponentContents();
  69.       for (let index: number = 0; index < this.overlayStorage.contentArray.length; index++) {
  70.         this.overlayManager.removeComponentContent(this.overlayStorage.contentArray[index])
  71.       }
  72.     }
  73.   }
  74. }
复制代码

初始化配置uiContext
  1. aboutToAppear(): void {
  2.     console.log("aboutToAppear");
  3.     MyOverlayManager.getInstance().initOverlayNode(this.getUIContext());
  4.   }
复制代码

定义type及message
  1. let config = new OverlayConfig(message);
  2.     config.type = OverlayType.loading;
  3.     config.onCallback = (index: number)=>{
  4.       MyOverlayManager.getInstance().removeOverlayView(index)
  5.     }
  6.     MyOverlayManager.getInstance().addOverlayView(config);
复制代码
加载中、加载乐成、加载失败的Util
  1. import { MyOverlayManager } from "../../manager/MyOverlayManager";import { OverlayConfig, OverlayType } from "../components/hud/CustomOverlayView";export class EasyLoadingHud {  static showLoading(message: string) {    let config = new OverlayConfig(message);
  2.     config.type = OverlayType.loading;
  3.     config.onCallback = (index: number)=>{
  4.       MyOverlayManager.getInstance().removeOverlayView(index)
  5.     }
  6.     MyOverlayManager.getInstance().addOverlayView(config);
  7.   }  static showSuccess(message: string) {    let config = new OverlayConfig(message);    config.type = OverlayType.success;    config.onCallback = (index: number)=>{      MyOverlayManager.getInstance().removeOverlayView(index)    }    MyOverlayManager.getInstance().addOverlayView(config);  }  static showFail(message: string) {    let config = new OverlayConfig(message);    config.type = OverlayType.fail;    config.onCallback = (index: number)=>{      MyOverlayManager.getInstance().removeOverlayView(index)    }    MyOverlayManager.getInstance().addOverlayView(config);  }}
复制代码

可以在页面必要的地方调用EasyLoadingHud进行显示
代码如下
  1. // 加载中
  2. EasyLoadingHud.showLoading("加载中...")
  3. // 加载成功
  4. EasyLoadingHud.showSuccess("加载成功")
  5. // 加载失败
  6. EasyLoadingHud.showFail("加载失败")
复制代码
四、小结

在开辟过程中会遇到提示弹窗、加载中指示器、加载失败的toast等功能,这里是学习HarmonyOS鸿蒙开辟的学习记录,假如对你有用,你可以点个赞哦~~。详细的文档还是以官方文档为主。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4