前言
不管在鸿蒙开发中还是在其他项目开发中,比如Android、iOS等其实弹窗都是必不可少的一个组件,在其他端中其实实现方式很多,也很优雅,代码零侵入。但是在鸿蒙开发中,其实跟网页开发实现方式差不多,弹窗基本会在布局中预先声明,在通过状态值来控制显示隐藏,当然也有非常厉害的可以把popup完全解耦出来。在鸿蒙中,因为生态还不够强大,很多东西还没有轮子,也没有可以找的资源,所以很多东西需要慢慢摸索。以下都以实现一个Loading举例:
1. 现有常见方案
常见的创建Loading弹窗有很多中,我只举例以下几种方案:
方案一
自定义一个Loading组件,在需要使用Loading的地方引入,在通过状态值控制显示隐藏
方案二
在需要Loading的界面根布局添加bindPopup属性,通过第一个参数来控制显示隐藏,当然也可以写在整个路由的根布局,通过全局状态参数来控制,这样比写在每一个界面还是好一点
方案三
自定义弹窗 CustomDialog,然后在需要弹窗的地方通过CustomDialogController控制器控制
方案四
通过创建子window方式创建
通过以上方案可以看出,每一种实现方式多儿不少都需要去改动原来的代码,这样使代码越来越复杂和杂乱,那有没有一种方案是代码零侵入,只需在调用的地方拉起弹窗呢?请接着往下看。
2. 零侵入方案
在声明式范式中,组件仅在build环节中被创建,开发者无法在其他生命周期阶段进行组件的创建,从而引起页面加载慢等问题。好在鸿蒙给出了解决方案UI动态操作,UI动态操作包含组件动态创建、卸载等相关操作。下面我们按照流程一步一步实现Loading弹窗。
创建自定义节点
export class PopupBean{
/**
* 进度提示
*/
hint:string
constructor(hint: string) {
this.hint = hint
}
}
@Builder
export function loadingBuilder(data: PopupBean) {
Column() {
LoadingProgress().width("80lpx").height("80lpx")
Text(data.hint).fontColor("#999999").margin("10lpx").fontSize("28lpx")
}
.backgroundColor(Color.White)
.padding("20lpx")
.borderRadius("10lpx")
.alignItems(HorizontalAlign.Center)
.justifyContent(FlexAlign.Center)
}
注意:这里loadingBuilder中的参数要不没有,要不就需要自定义一个类,不能使用string、number等
显示自定义节点
import { loadingBuilder } from '自己定义的路径';
import { ComponentContent, PromptAction, window } from '@kit.ArkUI';
import { PopupBean } from '自己定义的路径';
import { HashMap } from '@kit.ArkTS';
/**
* 进度加载管理
*/
export class LoadingManager {
private static instance: LoadingManager
private popupHashMap: HashMap<PromptAction, ComponentContent<PopupBean>>[] = []
private constructor() {
}
static getInstance(): LoadingManager {
if (LoadingManager.instance == null) {
LoadingManager.instance = new LoadingManager()
}
return LoadingManager.instance
}
/**
* 加载中...
* @param hint
*/
loading(hint: string = "加载中...") {
window.getLastWindow(getContext()).then((windowClass) => {
const uiContext = windowClass.getUIContext()
//这里去动态创建组件
let loadingPopup = new ComponentContent(uiContext, wrapBuilder(loadingBuilder), new PopupBean(hint))
//获取上下文中的PromptAction
let promptAction = uiContext.getPromptAction()
try {
//存储数据,方便关闭弹窗使用
let hashMap: HashMap<PromptAction, ComponentContent<PopupBean>> = new HashMap()
hashMap.set(promptAction, loadingPopup)
this.popupHashMap.push(hashMap)
//打开弹窗,这里openCustomDialog第二个参数可以自定义,这里只是Demo就不过多介绍了
promptAction.openCustomDialog(loadingPopup, {
alignment: DialogAlignment.Center,
isModal: false,
autoCancel: true
})
} catch (e) {
console.error(JSON.stringify(e))
}
})
}
/**
* 关闭弹窗
*/
dismiss() {
window.getLastWindow(getContext()).then((windowClass) => {
let size = this.popupHashMap.length
if (size == 0) {
return
}
let lastHashMap = this.popupHashMap[size - 1]
this.closePopup(lastHashMap)
this.popupHashMap.splice(size - 1, 1)
})
}
/**
* 关闭所有弹窗
*/
dismissAll() {
window.getLastWindow(getContext()).then((windowClass) => {
this.popupHashMap.forEach((hashMap: HashMap<PromptAction, ComponentContent<PopupBean>>) => {
this.closePopup(hashMap)
})
this.popupHashMap = []
})
}
/**
* 关闭弹窗
* @param hashMap
*/
private closePopup(hashMap: HashMap<PromptAction, ComponentContent<PopupBean>>) {
hashMap.forEach((loadingPopup: ComponentContent<PopupBean>, promptAction: PromptAction) => {
promptAction.closeCustomDialog(loadingPopup)
})
}
}
注意:这里关闭弹窗也需要在window.getLastWindow(getContext())中去执行,可以避免执行时序问题
导出组件
import { LoadingManager } from '自己定义的路径'
const Loading = LoadingManager.getInstance()
使用
import { Loading } from '自己定义的路径'
//自定义加载弹窗内容
Loading.loading("获取数据中...")
//使用默认加载弹窗内容
Loading.loading()
//关闭弹窗
Loading.dismiss()
//关闭所有弹窗
Loading.dismissAll()
至此,动态创建组件弹窗就完了,没有几句代码,按照项目要求自己实现效果就行了。
3.直接使用
ohpm i @chinalike/popup
写在最后
●如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我两个小忙:
●点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
●关注小编,同时可以期待后续文章ing ,不定期分享原创知识。