天空闲话 发表于 2025-1-11 20:48:37

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方法来实现。
实现步调
效果预览
https://i-blog.csdnimg.cn/direct/af59af593e61443a95cad786b157e2f6.jpeg#pic_center


[*]1、实现加载中的loading组件,这里定义名字为LoadingHud
在LoadingHud中有LoadingProgress、Text提示文本,Text显示的信息由LocalStorage进行传递
必要传递的数据message,在aboutToAppear进行赋值
@Local message: string = '';

aboutToAppear(): void {
    this.message = LocalStorage.getShared().get("message") ?? "";
}

固然在调用window的loadContentByName时候,必要确定加载的主角的routeName,这就必要在LoadingHud组件中使用装饰器来设置
/// 通用的hud,弹出框,或者loading框
@Entry({ routeName: "hudLoading", storage: LocalStorage.getShared() })
@ComponentV2
export struct LoadingHud {
... 其他代码
}

LoadingHud的完备代码如下:
/// 通用的hud,弹出框,大概loading框@Entry({ routeName: "hudLoading", storage: LocalStorage.getShared() })@ComponentV2export struct LoadingHud {@Local message: string = '';

aboutToAppear(): void {
    this.message = LocalStorage.getShared().get("message") ?? "";
}

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)}}

[*]2、创建子Window并显示
在创建LoadingHud后,我们必要创建创建子Window并显示window,显示我们的loadingHUD
创建window的createWindow,这里使用的windowType是window.WindowType.TYPE_DIALOG,也可以换成其他的试试看。
let windowName = "loading";
      // 创建窗口
      let subWindow = await window.createWindow(
      {
          name: windowName,
          windowType: window.WindowType.TYPE_DIALOG,
          ctx: ctx,
      }
      );

设置LocalStorage数据,存储message
//创建存储
      let storage = new LocalStorage();
      //存储数据
      storage.setOrCreate('message',tip);

调用window的loadContentByName,设置Window的大小及背景颜色,显示Window
await subWindow.loadContentByName('hudLoading', storage);
      let dp = display.getDefaultDisplaySync();
      await subWindow.resize(dp.width, dp.height);
      subWindow.setWindowBackgroundColor('#30000000');
      await subWindow.showWindow();

显示后Window,在必要消散的时候调用destroyWindow
static async dismissLoading(): Promise<void> {
    if (LoadingHudUtil.cacheWindow) {
      await LoadingHudUtil.cacheWindow.destroyWindow();
    }
}

完备的LoadingHudUtil的代码如下
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";
      // 创建窗口
      let subWindow = await window.createWindow(
      {
          name: windowName,
          windowType: window.WindowType.TYPE_DIALOG,
          ctx: ctx,
      }
      );

      LoadingHudUtil.cacheWindow = subWindow;      //创建存储
      let storage = new LocalStorage();
      //存储数据
      storage.setOrCreate('message',tip);

      console.log("LoadingHudUtil loadContentByName" + tip);      // subWindow.setGestureBackEnabled(false);      // subWindow.setDialogBackGestureEnabled(false);      // subWindow.setWindowTouchable(true);      await subWindow.loadContentByName('hudLoading', storage);
      let dp = display.getDefaultDisplaySync();
      await subWindow.resize(dp.width, dp.height);
      subWindow.setWindowBackgroundColor('#30000000');
      await subWindow.showWindow();

    } catch (e) {      console.log("LoadingHudUtil showLoading e:" + JSON.stringify(e));    }}static async dismissLoading(): Promise<void> {
    if (LoadingHudUtil.cacheWindow) {
      await LoadingHudUtil.cacheWindow.destroyWindow();
    }
}

} 二、自定义Dialog

在HarmonyOS鸿蒙开辟中,可以使用CustomDialogController来实现自定义的弹窗。
效果预览
https://i-blog.csdnimg.cn/direct/a0414924a8b645af80b1fc5edeb13fa7.jpeg#pic_center


[*]1.自定义弹窗组件CustomAlertDialog
在CustomAlertDialog中实现一个消息提示,并且点击按钮可以关闭dialog
代码如下:
@CustomDialog
export struct CustomAlertDialog {
controller?: CustomDialogController
title?: string

build() {
    Column() {
      Column() {
      // message
      Text(this.title)
          .fontSize(14)
          .fontColor($r('app.color.dataset_empty_message'))
          .margin({
            left: 10
          })

      Button("点击消失")
          .width(100)
          .height(40)
          .fontSize(12)
          .backgroundColor('#ef04792c')
          .margin({
            top: 10
          })
          .onClick(() => {
            this.controller?.close();
          })
      }
      .justifyContent(FlexAlign.Center)
      .constraintSize({
      minWidth: 200,
      minHeight: 100,
      })
      .backgroundColor($r('app.color.white'))
      .borderRadius(10)
    }
    .justifyContent(FlexAlign.Center)
    .width('100%')
    .height('100%')
    .backgroundColor(Color.Transparent)
    .hitTestBehavior(HitTestMode.Transparent)
}
}


[*]2.使用CustomDialogController来展示弹窗
定义CustomDialogController
// 自定义CustomDialog
customDialogController: CustomDialogController | null = new CustomDialogController({
    builder: CustomAlertDialog({
      title: "温馨提示"
    }),
    alignment: DialogAlignment.Center,
    onWillDismiss: (dismissDialogAction: DismissDialogAction) => {
      console.info("reason=" + JSON.stringify(dismissDialogAction.reason))
      console.log("dialog onWillDismiss")
      if (dismissDialogAction.reason == DismissReason.PRESS_BACK) {
      dismissDialogAction.dismiss()
      }
      if (dismissDialogAction.reason == DismissReason.TOUCH_OUTSIDE) {
      dismissDialogAction.dismiss()
      }
    },
    autoCancel: true,
    customStyle: true,
});

在必要展示弹窗的时候调用customDialogController的open方法。
if (this.customDialogController != null) {
                      this.customDialogController.open()
                  }
固然假如页面消散,只管在aboutToDisappear中将customDialogController置空
// 在自定义组件即将销毁时将dialogController置空
aboutToDisappear() {
    this.customDialogController = null // 将dialogController置空
}
三、Overlay浮层

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


[*]1.定义CustomOverlayView组件界面
在CustomOverlayView中,我们定义了加载中,加载失败,加载乐成的几种范例,用于展示不同的样式
定义OverlayConfig类为展示的界面配置、OverlayScaleImage缩放的icon
export enum OverlayType {
loading,
success,
fail
}

export class OverlayConfig {
message: string = ""
offset: Position = { x: 0, y: -50 }
index: number = 0
autoDismiss: boolean = true;
duration: number = 3000 // 持续时间
onCallback?: (index: number) => void
type: OverlayType = OverlayType.loading

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

@Builder
export function builderCustomOverlayView(overlayConfig: OverlayConfig) {
CustomOverlayView({
    olConfig: overlayConfig
})
}

@ComponentV2
struct OverlayScaleImage {
@Param @Require src: PixelMap | ResourceStr | DrawableDescriptor;
@Param imgWidth: number = 40;
@Local imgScale: number = 0.0;

build() {
    Image(this.src)
      .width(this.imgWidth)
      .aspectRatio(1)
      .scale({ x: this.imgScale, y: this.imgScale })
      .animation({
      duration: 300, // 时长
      iterations: 1, // 设置-1表示动画无限循环
      })
      .onAppear(() => {
      // 组件挂载完毕,修改数值触发动画效果
      this.imgScale = 1.0
      })
}
}

@ComponentV2
export struct CustomOverlayView {
@Param olConfig: OverlayConfig = new OverlayConfig("");

aboutToAppear(): void {
    setTimeout(() => {
      console.log("CustomOverlayView aboutToAppear");
      if (this.olConfig.onCallback != null) {
      this.olConfig.onCallback(this.olConfig.index);
      }
    }, this.olConfig.duration);
}

build() {
    Column() {
      if (OverlayType.loading == this.olConfig.type) {
      // message
      LoadingProgress()
          .color($r('app.color.success'))
          .width(40)
          .height(40)
      } else if (OverlayType.success == this.olConfig.type) {
      // message
      OverlayScaleImage({
          src: $r('app.media.ic_hud_success'),
          imgWidth: 40
      })
      } else if (OverlayType.fail == this.olConfig.type) {
      // message
      OverlayScaleImage({
          src: $r('app.media.ic_hud_fail'),
          imgWidth: 30
      })
      }

      Text(this.olConfig.message)
      .fontSize(14)
      .fontColor($r('app.color.white'))
      .margin({
          top: 10,
      })
    }
    .padding({
      top: 20,
      bottom: 20,
      left: 15,
      right: 15
    })
    .justifyContent(FlexAlign.Center)
    .constraintSize({
      minWidth: 180,
      minHeight: 80,
    })
    .backgroundColor($r('app.color.overlay_bg_color'))
    .borderRadius(10)
    .offset(this.olConfig.offset)
}
}


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


[*]3.自定义MyOverlayManager进行封装OverlayManager
起首确定属性uiContext,创建ComponentContent必要该参数,这个我在index.ets中进行初始化传入。
CustomOverlayStorage存储ComponentContent的数组,确定index
overlayManager用来来添加、删除、隐藏、显示节点
MyOverlayManager代码如下
/// 用于管理Overlay
/// 浮层(OverlayManager) 用于将自定义的UI内容展示在页面(Page)之上,
/// 在Dialog、Popup、Menu、BindSheet、BindContentCover和Toast等组件之下,
/// 展示的范围为当前窗口安全区内。可适用于常驻悬浮等场景。
/// 与OverlayManager相关的属性推荐采用AppStorage来进行应用全局存储,以免切换页面后属性值发生变化从而导致业务错误。
import { AppStorageV2, ComponentContent, OverlayManager, router } from '@kit.ArkUI';
import {
builderCustomOverlayView,
CustomOverlayStorage,
OverlayConfig
} from '../common/components/hud/CustomOverlayView';

export class MyOverlayManager {
private static currentIndex: number = 0;
private uiContext?: UIContext
private overlayManager?: OverlayManager
private overlayStorage: CustomOverlayStorage =
    AppStorageV2.connect(CustomOverlayStorage, 'overlayStorage', () => new CustomOverlayStorage())!;
private static instance: MyOverlayManager;

public static getInstance(): MyOverlayManager {
    if (MyOverlayManager.instance == null) {
      MyOverlayManager.instance = new MyOverlayManager();
    }
    return MyOverlayManager.instance;
}

initOverlayNode(uiContext: UIContext): void {
    this.uiContext = uiContext;
    this.overlayManager = uiContext.getOverlayManager();
}

addOverlayView(overlayConfig: OverlayConfig): void {
    if (this.uiContext != null && this.uiContext != undefined) {
      // 设置索引下标
      let index = MyOverlayManager.currentIndex++;
      overlayConfig.index = index;

      // 创建componentContent
      let componentContent = new ComponentContent(
      this.uiContext!, wrapBuilder<>(builderCustomOverlayView),
      overlayConfig
      )

      this.overlayStorage.contentArray.push(componentContent);

      if (this.overlayManager != null && this.overlayManager != undefined) {
      this.overlayManager.addComponentContent(componentContent, index)
      }
    }
}

hideOverlayView(index: number) {
    if (this.overlayManager != null && this.overlayManager != undefined) {
      if (index < this.overlayStorage.contentArray.length) {
      this.overlayManager.hideComponentContent(this.overlayStorage.contentArray)
      }
    }
}

showOverlayView(index: number) {
    if (this.overlayManager != null && this.overlayManager != undefined) {
      if (index < this.overlayStorage.contentArray.length) {
      this.overlayManager.showComponentContent(this.overlayStorage.contentArray)
      }
    }
}

removeOverlayView(index: number) {
    if (this.overlayManager != null && this.overlayManager != undefined) {
      if (index < this.overlayStorage.contentArray.length) {
      this.overlayManager.removeComponentContent(this.overlayStorage.contentArray)
      }
    }
}

removeAllOverlayView() {
    if (this.overlayManager != null && this.overlayManager != undefined) {
      this.overlayManager.hideAllComponentContents();
      for (let index: number = 0; index < this.overlayStorage.contentArray.length; index++) {
      this.overlayManager.removeComponentContent(this.overlayStorage.contentArray)
      }
    }
}
}


[*]4.index.ets传入uiContext
初始化配置uiContext
aboutToAppear(): void {
    console.log("aboutToAppear");
    MyOverlayManager.getInstance().initOverlayNode(this.getUIContext());
}



[*]5.调用OverlayManager进行显示加载中、加载乐成、加载失败提示
定义type及message
let config = new OverlayConfig(message);
    config.type = OverlayType.loading;
    config.onCallback = (index: number)=>{
      MyOverlayManager.getInstance().removeOverlayView(index)
    }
    MyOverlayManager.getInstance().addOverlayView(config);

加载中、加载乐成、加载失败的Util
import { MyOverlayManager } from "../../manager/MyOverlayManager";import { OverlayConfig, OverlayType } from "../components/hud/CustomOverlayView";export class EasyLoadingHud {static showLoading(message: string) {    let config = new OverlayConfig(message);
    config.type = OverlayType.loading;
    config.onCallback = (index: number)=>{
      MyOverlayManager.getInstance().removeOverlayView(index)
    }
    MyOverlayManager.getInstance().addOverlayView(config);

}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);}}

[*]6.页面调用EasyLoadingHud进行显示
可以在页面必要的地方调用EasyLoadingHud进行显示
代码如下
// 加载中
EasyLoadingHud.showLoading("加载中...")
// 加载成功
EasyLoadingHud.showSuccess("加载成功")
// 加载失败
EasyLoadingHud.showFail("加载失败")

四、小结

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

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: HarmonyOS鸿蒙开辟 弹窗及加载中指示器HUD功能实现