【鸿蒙5.0实战开发】在ArkTS中,实现不在Enter模块中就可以创建的自定义弹窗

326 篇文章 0 订阅
313 篇文章 0 订阅

在官方文档中,创建自定义弹窗是比较麻烦的,使用方式大概是:

1、使用@CustomDialog装饰的自定义组件作为弹窗的布局

2、在@CustomDialog装饰的自定义组件中必须声明CustomDialogControlle类型的变量

3、在需要显示弹窗的@Entry里面再次声明一个CustomDialogControlle类型的变量,并完成初始化

这样就可以通过@Entry里的CustomDialogControlle类型的变量进行控制弹窗的关闭和打开,但无法得知该弹窗打开/关闭状态。

开发过Android的小伙伴,已经熟悉了在哪里调用就在那里new一个Dialog直接弹出来的使用方式。所以为了迎合自己的使用习惯,就利用promptAction写了一个基类,实现这种调用方式。

第一步:创建基类

import { BusinessError } from '@kit.BasicServicesKit';
import { ComponentContent } from '@ohos.arkui.node';
import { uiObserver } from '@kit.ArkUI';

export abstract class BaseDialog<T extends object> {
    protected context: UIContext | null = null
    protected componentContent: ComponentContent<T> | null = null

    //是否允许点击三键back、左滑/右滑、键盘ESC的方式关闭弹窗
    protected isPressBackClose: boolean = true
    //是否允许点击遮障层的方式关闭弹窗
    protected isTouchOutsideClose: boolean = true
    //是否允许点击关闭按钮的方式关闭弹窗
    protected isCloseButtonClose: boolean = true
    //蒙层颜色
    protected maskColor?: ResourceColor
    //弹窗的位置
    protected alignment?: DialogAlignment;

    //弹窗是否已打开
    public isOpen: boolean = false

    constructor(context: UIContext) {
        this.context = context
        //监听UIContext的生命周期,在即将销毁时,关闭弹窗并
        this.context.getUIObserver()
            .off("routerPageUpdate", (routerPageInfo) => {
                if(routerPageInfo.state == uiObserver.RouterPageState.ABOUT_TO_DISAPPEAR){
                    this.destroy()
                }
            })

        this.init()
    }

    //初始化的时候可以调用
    public init(){

    }

    /**
     * 显示弹窗
     */
    public open() {
        //如果已经显示,就不要再创建了
        if(this.isOpen) return
        //有可能所依赖的UIContext被销毁了,就不要继续往下走了
        if(this.context == null) return

        try {
            this.componentContent = new ComponentContent<T>(this.context, this.create(), this.getParams())
            this.context
                .getPromptAction()
                .openCustomDialog(this.componentContent, {
                    onWillDismiss: this.onWillDismiss(),
                    maskColor: this.maskColor,
                    alignment:this.alignment
                })
                .then(() => this.isOpen = true)
        } catch (error) {
            let message = (error as BusinessError).message;
            let code = (error as BusinessError).code;
            console.error(`dddd args error code is ${code}, message is ${message}`);
        }
    }

    /**
     * 当用户执行左滑/右滑、三键back、键盘ESC关闭、点击遮障层关闭交互操作时,如果注册该回调函数,则不会立刻关闭弹窗
     *
     * 传送门:https://docs.openharmony.cn/pages/v5.0/zh-cn/application-dev/reference/apis-arkui/js-apis-arkui-observer.md#routerpageinfo
     */
    private onWillDismiss(){
        return (dismissDialogAction: DismissDialogAction) => {
            if(dismissDialogAction.reason == DismissReason.PRESS_BACK){
                if(this.isPressBackClose){
                    this.close()
                }
            }else if(dismissDialogAction.reason == DismissReason.TOUCH_OUTSIDE){
                if(this.isTouchOutsideClose){
                    this.close()
                }
            }else if(dismissDialogAction.reason == DismissReason.CLOSE_BUTTON){
                if(this.isCloseButtonClose){
                    this.close()
                }
            }else {

            }
        }
    }

    /**
     * 关闭弹窗
     */
    public close(){
        //有可能所依赖的UIContext被销毁了,就不要继续往下走了
        if(this.context == null) return
        //有可能弹窗被销毁了,就不要继续往下走了
        if(this.componentContent == null) return

        try {
            this.context
                .getPromptAction()
                .closeCustomDialog(this.componentContent)
                .then(() => this.isOpen = false)
        } catch (error) {
            let message = (error as BusinessError).message;
            let code = (error as BusinessError).code;
            console.error(`dddd args error code is ${code}, message is ${message}`);
        }
    }

    /**
     * 释放引用,避免内存泄漏
     */
    public destroy(){
        this.close()
        this.context = null
        this.componentContent = null
    }

    /**
     * 更新布局数据
     */
    public update(){
        this.componentContent?.update(this.getParams())
    }

    /**
     * 返回创建弹窗所需WrappedBuilder<T[]>
     *
     * @returns 需要调用wrapBuilder()方法传入@Builder修饰的方法返回即可
     */
    protected abstract create(): WrappedBuilder<T[]>

    /**
     * 获取传给组件的参数
     *
     * @returns 一般返回自身
     */
    protected abstract getParams() : T
}

第二步:继承基类,实现抽象方法,用加载弹窗举个栗子:

注:@Builder修饰的方法,可以单独写一个文件里,通过添加export暴露出来,所以不一定要写在弹窗的实现类里面,好处是实现视图与控制层分离,坏处是文件变多了

import { BaseDialog } from './BaseDialog'

@Builder
function Loading(params: LoadingDialog){
    Column(){
        Progress({ value: 0, total: 100, type: ProgressType.Ring })
            .color(Color.White)
            .style({ strokeWidth: 3, status: ProgressStatus.LOADING })
            .margin({bottom:5})
            .width(26)
            .height(26)

        Text(params.loadingText)
            .textAlign(TextAlign.Center)
            .fontSize(12)
            .fontColor(Color.White)
            .margin({top: 5})
    }
    .width(80)
    .height(80)
    .backgroundColor('#99292929')
    .borderRadius(5)
    .padding(10)
    .alignItems(HorizontalAlign.Center)
    .justifyContent(FlexAlign.Center)

}

//传递自身,方便组件可以调用弹窗的所有方法和属性
export class LoadingDialog extends BaseDialog<LoadingDialog>{
    loadingText: string = '加载中...'

    public init(): void {
        this.isPressBackClose = false
        this.isTouchOutsideClose = false
        this.isCloseButtonClose = false
        this.maskColor = Color.Transparent
    }

    protected create(): WrappedBuilder<LoadingDialog[]> {
        return wrapBuilder(Loading)
    }

    protected getParams(): LoadingDialog {
        return this
    }

    public setLoadingText(loadingText: string){
        this.loadingText = loadingText
        this.update()
    }
}

第三步:创建弹窗并调用

**注:**只要能拿到UIContext,LoadingDialog可以在任意地方完成创建,在@Entry里举栗子主要是说明UIContext可以在这里面获取到

@Entry
@Component
struct Example {
    //创建并初始化弹窗
    public loadingDialog: LoadingDialog = new LoadingDialog(this.getUIContext())

    //显示加载弹窗
    protected openLoadingDialog() {
        this.loadingDialog.open()
    }

    //隐藏加载弹窗
    protected closeLoadingDialog() {
        this.loadingDialog.close()
    }
}

好了,现在你可以愉快的自定义更多的弹窗了!

再多说一些:《蒙层颜色》《点击蒙层关闭事件》《弹窗是否已打开》都已在基类提供属性进行设置。

还想要设置更多奇奇怪怪的东西,可以查找promptAction.BaseDialogOptions的属性,然后在基类中,找到下方截图中所示位置进行设置:

image.png

写在最后

●如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
●点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
●关注小编,同时可以期待后续文章ing🚀,不定期分享原创知识。
●更多鸿蒙最新技术知识点,请移步前往小编:https://gitee.com/

在这里插入图片描述

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值