横扫鸿蒙弹窗乱象,SmartDialog出世
前言
但凡用过鸿蒙原生弹窗的小伙伴,就能体会到它们是有多么的难用和奇葩,什么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 模拟器目前有些问题,会导致动画闪烁,请忽略;注:真机动画丝滑流畅,无任何问题
极简用法
// 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 router Navigation(OhosSmartDialog.registerRouter(this.navPathStack)) { MainPage() } .mode(NavigationMode.Stack) .hideTitleBar(true) .navDestination(pageMap) // here OhosSmartDialog() }.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() { // show SmartDialog.config.custom.maskColor = "#75000000" SmartDialog.config.custom.alignment = Alignment.Center // showAttach SmartDialog.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 loading loadingBuilder: 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()) }
多位置弹窗
export async function customLocation() { const animationTime = 1000 SmartDialog.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()) }
跨页面交互
- 正常使用,无需设置什么参数
export function customJumpPage() { SmartDialog.show({ builder: dialogJumpPage, }) } @Builder function dialogJumpPage() { Text("JumPage") .fontSize(30) .padding(50) .borderRadius(12) .fontColor(Color.White) .backgroundColor(randomColor()) .onClick(() => { // 跳转页面 }) }
关闭指定弹窗
export async function customTag() { const animationTime = 1000 SmartDialog.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) }
自定义遮罩
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()) }
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()) }
多方向定位
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()) }
Loading
对于Loading而言,应该有几个比较明显的特性
- loading和dialog都存在页面上,哪怕dialog打开,loading都应该显示dialog之上
- loading应该具有单一特性,多次打开loading,页面也应该只存在一个loading
- 刷新特性,多次打开loading,后续打开的loading样式,应该覆盖之前打开的loading样式
- loading使用频率非常高,应该支持强大的拓展和极简的使用
从上面列举几个特性而言,loading是一个非常特殊的dialog,所以需要针对其特性,进行定制化的实现
当然了,内部已经屏蔽了细节,在使用上,和dialog的使用没什么区别
默认loading
SmartDialog.showLoading()
自定义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()) }
最后
鸿蒙版的SmartDialog,相信会对开发鸿蒙的小伙伴们有一些帮助~.~
现在就业环境真是让人头皮发麻,现在的各种技术群里,看到好多人公司各种拖欠工资,各种失业半年的情况
淦,不知道还能写多长时间代码!

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
BootstrapBlazor v8.8.2 发布,Bootstrap 样式的 Blazor UI 组件库
BootstrapBlazor v8.8.2 已经发布,Bootstrap 样式的 Blazor UI 组件库 此版本更新内容包括: Release 2024-8-11 V8.8.2 Bugs fix(TimePicker): 修复弹窗中无法调节时间问题 by @ArgoZhang in https://github.com/dotnetcore/BootstrapBlazor/pull/3975 fix(Table): 修复首次加载不可见时高度设置不正确问题 by @ArgoZhang in https://github.com/dotnetcore/BootstrapBlazor/pull/3980 fix(Layout): 修复设置 SideWidth 参数后导致侧边栏无法收起问题 by @SamXMG in https://github.com/dotnetcore/BootstrapBlazor/pull/3977 fix(Table): 修复 Table 组件调整父容器大小时脚本报错问题 by @ArgoZhang in https://github.com/dotnetc...
- 下一篇
Advanced RAG 11:对用户输入的内容进行「分类处理」和「再优化」
编者按: 你是否曾经遇到过这些情况:你向 AI 助手提出了一个比较复杂的问题,但它给出的回答却比较浅显,甚至完全偏离了你的意图🤔?或者,你询问了一个非常简单的问题, AI 助手却给出了一大堆不必要的信息,让你感到烦恼😣? 传统的 RAG 技术虽然能有效减少 AI 回答内容中的错误,但并不能改进用户最初提交的 query 内容,因此可能会出现以下这些问题: 对于用户提交的简单 query ,系统可能会消耗过多的计算资源,浪费用户时间和增加资源消耗。 面对复杂的 query,直接使用原始的 query 进行检索往往无法整理到足够的信息,导致回答不完整或不准确。 对于含义模糊、模棱两可的 query ,仅凭原始的 query 进行信息检索更是远远不够,可能会误解用户的真实意图。 那么,我们如何才能缓解这些问题,提高 AI 系统的理解能力和回答质量呢?本文将介绍两种技术方案:query classification 和 query refinement,并通过代码实例加以阐释,同时还在本文中记录了作者对这些技术方案的理解和思考内容。 作者 | Florian June 编译 | 岳扬 目录...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- MySQL8.0.19开启GTID主从同步CentOS8
- Mario游戏-低调大师作品
- Linux系统CentOS6、CentOS7手动修改IP地址
- Docker安装Oracle12C,快速搭建Oracle学习环境
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- CentOS7安装Docker,走上虚拟化容器引擎之路
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题