首页 > 其他分享 >横扫鸿蒙弹窗乱象,SmartDialog出世

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

时间:2024-08-11 11:27:54浏览次数:9  
标签:function loading 鸿蒙 Color SmartDialog 弹窗 dialog alignment

前言

但凡用过鸿蒙原生弹窗的小伙伴,就能体会到它们是有多么的难用和奇葩,什么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

标签:function,loading,鸿蒙,Color,SmartDialog,弹窗,dialog,alignment
From: https://www.cnblogs.com/xdd666/p/18353193

相关文章

  • 鸿蒙(HarmonyOS)实现隐私政策弹窗
    在实现用户协议弹窗时,通常我们会想到使用系统自定义弹窗,并在弹窗中点击跳转到Web页面。但在HarmonyOS中,由于系统弹窗的显示优先级高于其他组件,即使跳转到Web页面,弹窗依然会显示在最上层。为了解决这个问题,我们可以自定义一个组件来模拟弹窗,这样当跳转到Web页面时,Web内容会覆盖这......
  • 《永劫无间》游戏崩溃提示缺少steam_api.dll文件怎么处理?永劫无间游戏弹窗“找不到ste
    在玩《永劫无间》时,如果遇到游戏崩溃并提示缺少steam_api.dll文件,这是一个常见的问题。可以尝试重新安装Steam平台,或者从可靠渠道获取该文件并放置到正确的游戏目录下,以恢复游戏的正常运行。本篇将为大家带来《永劫无间》游戏崩溃提示缺少steam_api.dll文件修复方法的内容,感兴......
  • 《英雄联盟》游戏闪退提示“base.dll文件损坏”怎么修复?英雄联盟游戏崩溃弹窗base.dll
    在《英雄联盟》遇到因“base.dll文件损坏”而导致的游戏闪退问题时,可以先尝试重新安装游戏,这能覆盖可能损坏的文件。还可以使用系统的文件修复工具,对该文件进行检测和修复,或者从可靠来源获取该文件进行替换。本篇将为大家带来《英雄联盟》游戏闪退提示“base.dll文件损坏”修复......
  • 电脑弹窗“找不到libmmd.dll文件”怎么处理?Win11系统缺少libmmd.dll文件的修复方法一
    当电脑弹窗提示“找不到libmmd.dll文件”时,有几种可行的修复方法。可以在网上搜索该文件并下载,将其放置到正确的系统目录中。也可以通过运行系统文件检查工具,对缺失的文件进行扫描和修复,恢复系统的正常运行。本篇将为大家带来电脑弹窗“找不到libmmd.dll文件”的内容,感兴趣的小......
  • 《NBA2K24》弹窗提示“D3D Creation Failed错误”怎么处理?NBA2K24游戏闪退显示D3D Cre
    在NBA2K24游戏中遇到“D3DCreationFailed”错误导致的闪退问题时,可尝试多种修复方法。首先更新显卡驱动至最新版本,检查DirectX是否需要更新,还可以调整游戏内的图形设置,降低画质等,这些措施或许能解决闪退问题。本篇将为大家带来NBA2K24游戏闪退D3DCreationFailed错误修复方......
  • 鸿蒙HarmonyOS NEXT开发:图形变换(ArkTS通用属性)
    图形变换用于对组件进行旋转、平移、缩放、矩阵变换等操作。说明:从APIVersion7开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。rotaterotate(value:RotateOptions)设置组件旋转。卡片能力: 从APIversion9开始,该接口支持在ArkTS卡片中使用......
  • 鸿蒙HarmonyOS NEXT开发:形状裁剪(ArkTS通用属性)
    形状裁剪用于对组件进行裁剪、遮罩处理。说明:从APIVersion7开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。clip12+clip(value:boolean)是否对当前组件进行裁剪。系统能力: SystemCapability.ArkUI.ArkUI.Full参数:参数名类型必填说明valu......
  • 鸿蒙HarmonyOS NEXT开发:颜色渐变(ArkTS通用属性)
    颜色渐变设置组件的颜色渐变效果。说明:从APIVersion7开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。linearGradientlinearGradient(value:{angle?:number|string;direction?:GradientDirection;colors:Array<[ResourceColor,number]......
  • 鸿蒙HarmonyOS NEXT开发:菜单控制(ArkTS通用属性)
    菜单控制为组件绑定弹出式菜单,弹出式菜单以垂直列表形式显示菜单项,可通过长按、点击或鼠标右键触发。说明:从APIVersion7开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。CustomBuilder里不支持再使用bindMenu、bindContextMenu弹出菜单。多级菜......
  • 鸿蒙HarmonyOS NEXT开发:悬浮态效果(ArkTS通用属性)
    悬浮态效果设置组件的鼠标悬浮态显示效果。说明:从APIVersion8开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。hoverEffecthoverEffect(value:HoverEffect)设置组件的鼠标悬浮态显示效果。原子化服务API: 从APIversion11开始,该接口支持在......