横扫鸿蒙弹窗乱象,SmartDialog出世

news/2024/11/15 13:41:13/文章来源:https://www.cnblogs.com/xdd666/p/18353193

前言

但凡用过鸿蒙原生弹窗的小伙伴,就能体会到它们是有多么的难用和奇葩,什么AlertDialog,CustomDialog,SubWindow,bindXxx,只要大家用心去体验,就能发现他们有很多离谱的设计和限制,时常就是一边用,一边骂骂咧咧的吐槽

实属无奈,就把鸿蒙版的SmartDialog写出来了

flutter的自带的dialog是可以应对日常场景,例如:简单的打开一个弹窗,非UI模块使用,跨页面交互之类;flutter_smart_dialog 是补齐了大多数的业务场景和一些强大的特殊能力,flutter_smart_dialog 对于flutter而言,日常场景是锦上添花,特殊场景是雪中送炭

但是 ohos_smart_dialog 对于鸿蒙而言,日常场景就是雪中送炭!单单一个使用方式而言,就是吊打鸿蒙的CustomDialog,CustomDialog的各种限制和使用方式,我不想再去提及和吐槽了

有时候,简洁的使用,才是最大的魅力

鸿蒙版的SmartDialog有什么优势?

  • 单次初始化后即可使用,无需多处配置相关Component
  • 优雅,极简的用法
  • 非UI区域内使用,自定义Component
  • 返回事件处理,优化的跨页面交互
  • 多弹窗能力,多位置弹窗:上下左右中间
  • 定位弹窗:自动定位目标Component
  • 极简用法的loading弹窗
  • 等等......

目前 flutter_smart_dialog 的代码量16w+,完整复刻其功能,工作量非常大,目前只能逐步实现一些基础能力,由于鸿蒙api的设计和相关限制,用法和相关初始化都有一定程度的妥协

鸿蒙版本的SmartDialog,功能会逐步和 flutter_smart_dialog 对齐(长期),api会尽量保持一致

效果

  • Tablet 模拟器目前有些问题,会导致动画闪烁,请忽略;注:真机动画丝滑流畅,无任何问题

attachLocation

customTag

customJumpPage

极简用法

// dialog
SmartDialog.show({builder: dialogArgs,builderArgs: Math.random(),
})@Builder
function dialogArgs(args: number) {Text(args.toString()).padding(50).backgroundColor(Color.White)
}// loading
SmartDialog.showLoading()

安装

  • github:https://github.com/xdd666t/ohos_smart_dialog
  • ohos:https://ohpm.openharmony.cn/#/cn/detail/ohos_smart_dialog
ohpm install ohos_smart_dialog 

配置

下述的配置项,可能会有一点多,但,这也是为了极致的体验;同时也是无奈之举,相关配置难以在内部去闭环处理,只能在外部去配置

这些配置,只需要配置一次,后续无需关心

完成下述的配置后,你将可以在任何地方使用弹窗,没有任何限制

初始化

  • 因为弹窗需要处理跨页面交互,必须要监控路由
@Entry
@Component
struct Index {navPathStack: NavPathStack = new NavPathStack()build() {Stack() {// here: monitor routerNavigation(OhosSmartDialog.registerRouter(this.navPathStack)) {MainPage()}.mode(NavigationMode.Stack).hideTitleBar(true).navDestination(pageMap)// hereOhosSmartDialog()}.height('100%').width('100%')}
}

返回事件监听

别问我为啥返回事件的监听,处理的这么不优雅,鸿蒙里面没找全局返回事件监听,我也没辙。。。

  • 如果你无需处理返回事件,可以使用下述写法
// Entry页面处理
@Entry
@Component
struct Index {onBackPress(): boolean | void {return OhosSmartDialog.onBackPressed()()}
}// 路由子页面
struct JumpPage {build() {NavDestination() {// ....}.onBackPressed(OhosSmartDialog.onBackPressed())}
}
  • 如果你需要处理返回事件,在OhosSmartDialog.onBackPressed()中传入你的方法即可
// Entry页面处理
@Entry
@Component
struct Index {onBackPress(): boolean | void {return OhosSmartDialog.onBackPressed(this.onCustomBackPress)()}onCustomBackPress(): boolean {return false}
}// 路由子页面
@Component
struct JumpPage {build() {NavDestination() {// ...}.onBackPressed(OhosSmartDialog.onBackPressed(this.onCustomBackPress))}onCustomBackPress(): boolean {return false}
}

路由监听

  • 一般来说,你无需关注SmartDialog的路由监听,因为内部已经设置了路由监听拦截器
  • 但是,NavPathStack仅支持单拦截器(setInterception),如果业务代码也使用了这个api,会导致SmartDialog的路由监听被覆盖,从而失效

如果出现该情况,请参照下述解决方案

  • 在你的路由监听类中手动调用OhosSmartDialog.observe
export default class YourNavigatorObserver implements NavigationInterception {willShow?: InterceptionShowCallback = (from, to, operation, isAnimated) => {OhosSmartDialog.observe.willShow?.(from, to, operation, isAnimated)// ...}didShow?: InterceptionShowCallback = (from, to, operation, isAnimated) => {OhosSmartDialog.observe.didShow?.(from, to, operation, isAnimated)// ...}
}

适配暗黑模式

  • 为了极致的体验,深色模式切换时,打开态弹窗也应刷新为对应模式的样式,故需要进行下述配置
export default class EntryAbility extends UIAbility {  onConfigurationUpdate(newConfig: Configuration): void {  OhosSmartDialog.onConfigurationUpdate(newConfig)  }  
}

SmartConfig

  • 支持全局配置弹窗的默认属性
function init() {// showSmartDialog.config.custom.maskColor = "#75000000"SmartDialog.config.custom.alignment = Alignment.Center// showAttachSmartDialog.config.attach.attachAlignmentType = SmartAttachAlignmentType.center
}
  • 检查弹窗是否存在
// 检查当前是否有CustomDialog,AttachDialog或LoadingDialog处于打开状态
let isExist = SmartDialog.checkExist()// 检查当前是否有AttachDialog处于打开状态
let isExist = SmartDialog.checkExist({ dialogTypes: [SmartAllDialogType.attach] })// 检查当前是否有tag为“xxx”的dialog处于打开状态
let isExist = SmartDialog.checkExist({ tag: "xxx" })

配置全局默认样式

  • ShowLoading 自定样式十分简单
SmartDialog.showLoading({ builder: customLoading })

但是对于大家来说,肯定是想用 SmartDialog.showLoading() 这种简单写法,所以支持自定义全局默认样式

  • 需要在 OhosSmartDialog 上配置自定义的全局默认样式
@Entry
@Component
struct Index {build() {Stack() {OhosSmartDialog({// custom global loadingloadingBuilder: customLoading,})}.height('100%').width('100%')}
}@Builder
export function customLoading(args: ESObject) {LoadingProgress().width(80).height(80).color(Color.White)
}
  • 配置完你的自定样式后,使用下述代码,就会显示你的 loading 样式
SmartDialog.showLoading()// 支持入参,可以在特殊场景下灵活配置
SSmartDialog.showLoading({ builderArgs: 1 })

CustomDialog

  • 下方会共用的方法
export function randomColor(): string {const letters: string = '0123456789ABCDEF';let color = '#';for (let i = 0; i < 6; i++) {color += letters[Math.floor(Math.random() * 16)];}return color;
}export function delay(ms?: number): Promise<void> {return new Promise(resolve => setTimeout(resolve, ms));
}

传参弹窗

export function customUseArgs() {SmartDialog.show({builder: dialogArgs,// 支持任何类型builderArgs: Math.random(),})
}@Builder
function dialogArgs(args: number) {Text(`${args}`).fontColor(Color.White).padding(50).borderRadius(12).backgroundColor(randomColor())
}

customUseArgs

多位置弹窗

export async function customLocation() {const animationTime = 1000SmartDialog.show({builder: dialogLocationHorizontal,alignment: Alignment.Start,})await delay(animationTime)SmartDialog.show({builder: dialogLocationVertical,alignment: Alignment.Top,})
}@Builder
function dialogLocationVertical() {Text("location").width("100%").height("20%").fontSize(20).fontColor(Color.White).textAlign(TextAlign.Center).padding(50).backgroundColor(randomColor())
}@Builder
function dialogLocationHorizontal() {Text("location").width("30%").height("100%").fontSize(20).fontColor(Color.White).textAlign(TextAlign.Center).padding(50).backgroundColor(randomColor())
}

customLocation

跨页面交互

  • 正常使用,无需设置什么参数
export function customJumpPage() {SmartDialog.show({builder: dialogJumpPage,})
}@Builder
function dialogJumpPage() {Text("JumPage").fontSize(30).padding(50).borderRadius(12).fontColor(Color.White).backgroundColor(randomColor()).onClick(() => {// 跳转页面})
}

customJumpPage

关闭指定弹窗

export async function customTag() {const animationTime = 1000SmartDialog.show({builder: dialogTagA,alignment: Alignment.Start,tag: "A",})await delay(animationTime)SmartDialog.show({builder: dialogTagB,alignment: Alignment.Top,tag: "B",})
}@Builder
function dialogTagA() {Text("A").width("20%").height("100%").fontSize(20).fontColor(Color.White).textAlign(TextAlign.Center).padding(50).backgroundColor(randomColor())
}@Builder
function dialogTagB() {Flex({ wrap: FlexWrap.Wrap }) {ForEach(["closA", "closeSelf"], (item: string, index: number) => {Button(item).backgroundColor("#4169E1").margin(10).onClick(() => {if (index === 0) {SmartDialog.dismiss({ tag: "A" })} else if (index === 1) {SmartDialog.dismiss({ tag: "B" })}})})}.backgroundColor(Color.White).width(350).margin({ left: 30, right: 30 }).padding(10).borderRadius(10)
}

customTag

自定义遮罩

export function customMask() {SmartDialog.show({builder: dialogShowDialog,maskBuilder: dialogCustomMask,})
}@Builder
function dialogCustomMask() {Stack().width("100%").height("100%").backgroundColor(randomColor()).opacity(0.6)
}@Builder
function dialogShowDialog() {Text("showDialog").fontSize(30).padding(50).fontColor(Color.White).borderRadius(12).backgroundColor(randomColor()).onClick(() => customMask())
}

customMask

AttachDialog

默认定位

export function attachEasy() {SmartDialog.show({builder: dialog})
}@Builder
function dialog() {Stack() {Text("Attach").backgroundColor(randomColor()).padding(20).fontColor(Color.White).borderRadius(5).onClick(() => {SmartDialog.showAttach({targetId: "Attach",builder: targetLocationDialog,})}).id("Attach")}.borderRadius(12).padding(50).backgroundColor(Color.White)
}@Builder
function targetLocationDialog() {Text("targetIdDialog").fontSize(20).fontColor(Color.White).textAlign(TextAlign.Center).padding(50).borderRadius(12).backgroundColor(randomColor())
}

attachEasy

多方向定位

export function attachLocation() {SmartDialog.show({builder: dialog})
}class AttachLocation {title: string = ""alignment?: Alignment
}const locationList: Array<AttachLocation> = [{ title: "TopStart", alignment: Alignment.TopStart },{ title: "Top", alignment: Alignment.Top },{ title: "TopEnd", alignment: Alignment.TopEnd },{ title: "Start", alignment: Alignment.Start },{ title: "Center", alignment: Alignment.Center },{ title: "End", alignment: Alignment.End },{ title: "BottomStart", alignment: Alignment.BottomStart },{ title: "Bottom", alignment: Alignment.Bottom },{ title: "BottomEnd", alignment: Alignment.BottomEnd },
]@Builder
function dialog() {Column() {Grid() {ForEach(locationList, (item: AttachLocation) => {GridItem() {buildButton(item.title, () => {SmartDialog.showAttach({targetId: item.title,alignment: item.alignment,maskColor: Color.Transparent,builder: targetLocationDialog})})}})}.columnsTemplate('1fr 1fr 1fr').height(220)buildButton("allOpen", async () => {for (let index = 0; index < locationList.length; index++) {let item = locationList[index]SmartDialog.showAttach({targetId: item.title,alignment: item.alignment,maskColor: Color.Transparent,builder: targetLocationDialog,})await delay(300)}}, randomColor())}.borderRadius(12).width(700).padding(30).backgroundColor(Color.White)
}@Builder
function buildButton(title: string, onClick?: VoidCallback, bgColor?: ResourceColor) {Text(title).backgroundColor(bgColor ?? "#4169E1").constraintSize({ minWidth: 120, minHeight: 46 }).margin(10).textAlign(TextAlign.Center).fontColor(Color.White).borderRadius(5).onClick(onClick).id(title)
}@Builder
function targetLocationDialog() {Text("targetIdDialog").fontSize(20).fontColor(Color.White).textAlign(TextAlign.Center).padding(50).borderRadius(12).backgroundColor(randomColor())
}

attachLocation

Loading

对于Loading而言,应该有几个比较明显的特性

  • loading和dialog都存在页面上,哪怕dialog打开,loading都应该显示dialog之上
  • loading应该具有单一特性,多次打开loading,页面也应该只存在一个loading
  • 刷新特性,多次打开loading,后续打开的loading样式,应该覆盖之前打开的loading样式
  • loading使用频率非常高,应该支持强大的拓展和极简的使用

从上面列举几个特性而言,loading是一个非常特殊的dialog,所以需要针对其特性,进行定制化的实现

当然了,内部已经屏蔽了细节,在使用上,和dialog的使用没什么区别

默认loading

SmartDialog.showLoading()

loadingDefault

自定义Loading

  • 点击loading后,会再次打开一个loading,从效果图可以看出它的单一刷新特性
export function loadingCustom() {SmartDialog.showLoading({builder: customLoading,})
}@Builder
export function customLoading() {Column({ space: 5 }) {Text("again open loading").fontSize(16).fontColor(Color.White)LoadingProgress().width(80).height(80).color(Color.White)}.padding(20).borderRadius(12).onClick(() => loadingCustom()).backgroundColor(randomColor())
}

loadingCustom

最后

鸿蒙版的SmartDialog,相信会对开发鸿蒙的小伙伴们有一些帮助.

现在就业环境真是让人头皮发麻,现在的各种技术群里,看到好多人公司各种拖欠工资,各种失业半年的情况

淦,不知道还能写多长时间代码!

004B5DB3

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/781204.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

XSS 专项

访问web应用,首页HTML发现新端点如下:使用《赏猎技战法》https://www.cnblogs.com/sec875/p/18335838 中的 测试 XSS 流程,使用一个良性payload试试水:<h1>666 同时思考此处的功能点:https://cfceb12f2bfd-sec875.a.barker-social.com/post/1 post作为输入时和1作为…

Skeleton Recall Loss 分割领域的新突破:极大的减少了资源消耗,还能提高性能

精确分割在当今众多领域都是一项关键需求比如说自动驾驶汽车的训练、医学图像识别系统,以及通过卫星图像进行监测。在许多其他领域,当感兴趣的对象微小但至关重要时,例如研究血管流动、手术规划、检测建筑结构中的裂缝或优化路线规划,需要更高的精度。此前已经做了大量工作…

Unity脚本生命周期

生命周期函数的概念 所有继承MonoBehavior的脚本,最终都会挂在到GameObject游戏对象上 生命周期函数,就是该脚本对象依附的GameObject对象从出生到消亡整个生命周期中 会通过反射自动调用一些特殊函数 Unity帮助我们记录了一个GameObject对象依附了哪些脚本 会自动的得到这些…

掌握 Nuxt 3 的页面元数据:使用 definePageMeta 进行自定义配置

title: 掌握 Nuxt 3 的页面元数据:使用 definePageMeta 进行自定义配置 date: 2024/8/11 updated: 2024/8/11 author: cmdragon excerpt: 摘要:本文详细介绍Nuxt 3框架中definePageMeta的使用方法,包括如何为页面组件定义元数据,如布局、过渡效果、路由中间件等。通过具体…

HTB-Permx靶机笔记

Permx靶机笔记 概述 permx靶机是HTB的简单靶机,这台靶机整体考验渗透人员的信息搜集能力,可以收只有信息搜集的快速,才能快速拿到它的flag。 整体是比较简单的靶机 靶机连接:https://app.hackthebox.com/machines/PermX 一、nmap扫描 1)端口扫描 nmap -sT --min-rate 1000…

BMC Genomics | 火龙果的转录组和代谢组分析揭示了果皮和果肉颜色形成的机制

阐明火龙果果肉和果皮变色的候选基因和关键代谢产物,是培育具有优良新口味和高营养价值的火龙果的必要条件。在这里,使用转录组(RNA-Seq)和代谢组分析(UPLC-MS/MS)鉴定了属于两种不同量天尺属物种的三种火龙果的结构和调控基因以及与果皮和果肉颜色相关的关键代谢物。作者综合…

USB协议详解第3讲(USB描述符-设备描述符)

我们第一个学习要点就是USB描述符,所谓描述符其实就是C语言里面的结构体或者数组,数组包含的信息说明当前的设备具有哪些特征。USB描述符有设备描述符、配置描述符、接口描述符、端点描述符、字符串描述符,HID设备有HID描述符、报告描述符和物理描述符。我们先学会每个描述符…

Windows如何保证所有软件在D(其他)盘

Windows如何保证所有软件在D(其他)盘首先使用你电脑有两个固态硬盘就是盘符自己分区也可以 C盘我们只用于电脑的系统资源管理其他所有数据包括默认安装的文件都在D盘或者自定义的盘 win+R 输入 regedit 进入注册表根据下面路径依次点开,找到CurrentVersion,或者复制路径,粘贴…

【笔记】【THM】Malware Analysis(恶意软件分析)

二进制安全入坟【笔记】【THM】Malware Analysis(恶意软件分析) 探索恶意软件的世界,分析恶意软件如何感染系统并造成破坏。 恶意软件分析就像猫捉老鼠的游戏。恶意软件的作者一直在设计新的技术来躲避恶意软件分析师的眼睛,而恶意软件分析师也一直在寻找识别和抵消这些技术…

《逆行人生》电影迅雷下载3.69GB/MP4(百度云磁力资源共享)HD高清版

在光怪陆离的电影世界里,总有一些作品能够触动人心,让人在欢笑与泪水中重新审视生活的意义。《逆行人生》就是这样一部电影,它不仅仅是一部简单的现实题材作品,更是一次对人性光辉和社会现实的深刻探讨。由徐峥执导并主演,这部电影汇聚了冯兵、贾冰、王骁、丁勇岱等众多实…

[图文直播]Windows操作系统部署Jenkins

前言 首先说明一下我为什么选择在Windows操作系统上部署Jenkins是吧,主要基于虽然从长远上看,我是有进行跨平台开发的需求,但至少在可预见的三到五年时间内,我的潜在客户也都是在windows操作系统上。至于跨平台,规划上要有,但正如天龙八部里天龙寺内面对鸠摩智打算拿拈花…