横扫鸿蒙弹窗乱象,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 模拟器目前有些问题,会导致动画闪烁,请忽略;注:真机动画丝滑流畅,无任何问题

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

安装

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

customUseArgs

多位置弹窗

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

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

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


__EOF__

本文作者小呆呆
本文链接https://www.cnblogs.com/xdd666/p/18353193.html
关于博主:评论和私信会在第一时间回复。或者 直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角 推荐一下。您的鼓励是博主的最大动力!
原创作者: xdd666 转载于: https://www.cnblogs.com/xdd666/p/18353193
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值