橫掃鴻蒙彈窗亂象,SmartDialog出世

前言

但凡用過(guò)鴻蒙原生彈窗的小伙伴,就能體會(huì)到它們是有多么的難用和奇葩,什么AlertDialog,CustomDialog,SubWindow,bindXxx,只要大家用心去體驗(yàn),就能發(fā)現(xiàn)他們有很多離譜的設(shè)計(jì)和限制,時(shí)常就是一邊用,一邊罵罵咧咧的吐槽

實(shí)屬無(wú)奈,就把鴻蒙版的SmartDialog寫(xiě)出來(lái)了

flutter的自帶的dialog是可以應(yīng)對(duì)日常場(chǎng)景,例如:簡(jiǎn)單的打開(kāi)一個(gè)彈窗,非UI模塊使用,跨頁(yè)面交互之類(lèi);flutter_smart_dialog 是補(bǔ)齊了大多數(shù)的業(yè)務(wù)場(chǎng)景和一些強(qiáng)大的特殊能力,flutter_smart_dialog 對(duì)于flutter而言,日常場(chǎng)景是錦上添花,特殊場(chǎng)景是雪中送炭

但是 ohos_smart_dialog 對(duì)于鴻蒙而言,日常場(chǎng)景就是雪中送炭!單單一個(gè)使用方式而言,就是吊打鴻蒙的CustomDialog,CustomDialog的各種限制和使用方式,我不想再去提及和吐槽了

有時(shí)候,簡(jiǎn)潔的使用,才是最大的魅力

鴻蒙版的SmartDialog有什么優(yōu)勢(shì)?

  • 單次初始化后即可使用,無(wú)需多處配置相關(guān)Component
  • 優(yōu)雅,極簡(jiǎn)的用法
  • 非UI區(qū)域內(nèi)使用,自定義Component
  • 返回事件處理,優(yōu)化的跨頁(yè)面交互
  • 多彈窗能力,多位置彈窗:上下左右中間
  • 定位彈窗:自動(dòng)定位目標(biāo)Component
  • 極簡(jiǎn)用法的loading彈窗
  • 等等......

目前 flutter_smart_dialog 的代碼量16w+,完整復(fù)刻其功能,工作量非常大,目前只能逐步實(shí)現(xiàn)一些基礎(chǔ)能力,由于鴻蒙api的設(shè)計(jì)和相關(guān)限制,用法和相關(guān)初始化都有一定程度的妥協(xié)

鴻蒙版本的SmartDialog,功能會(huì)逐步和 flutter_smart_dialog 對(duì)齊(長(zhǎng)期),api會(huì)盡量保持一致

效果

  • Tablet 模擬器目前有些問(wèn)題,會(huì)導(dǎo)致動(dòng)畫(huà)閃爍,請(qǐng)忽略;注:真機(jī)動(dòng)畫(huà)絲滑流暢,無(wú)任何問(wèn)題

[圖片上傳失敗...(image-657c72-1723348012423)]

[圖片上傳失敗...(image-5db6d2-1723348012423)]

[圖片上傳失敗...(image-902a8f-1723348012424)]

極簡(jiǎn)用法

// 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 

配置

下述的配置項(xiàng),可能會(huì)有一點(diǎn)多,但,這也是為了極致的體驗(yàn);同時(shí)也是無(wú)奈之舉,相關(guān)配置難以在內(nèi)部去閉環(huán)處理,只能在外部去配置

這些配置,只需要配置一次,后續(xù)無(wú)需關(guān)心

完成下述的配置后,你將可以在任何地方使用彈窗,沒(méi)有任何限制

初始化

  • 注:內(nèi)部已使用無(wú)感路由注冊(cè),外部無(wú)需手動(dòng)處理
@Entry
@Component
struct Index {
  navPathStack: NavPathStack = new NavPathStack()

  build() {
    Stack() {
      Navigation(this.navPathStack) {
        MainPage()
      }
      .mode(NavigationMode.Stack)
      .hideTitleBar(true)
      .navDestination(pageMap)

      // here dialog init
      OhosSmartDialog()
    }.height('100%').width('100%')
  }
}

返回事件監(jiān)聽(tīng)

別問(wèn)我為啥返回事件的監(jiān)聽(tīng),處理的這么不優(yōu)雅,鴻蒙里面沒(méi)找全局返回事件監(jiān)聽(tīng),我也沒(méi)轍。。。

  • 如果你無(wú)需處理返回事件,可以使用下述寫(xiě)法
// Entry頁(yè)面處理
@Entry
@Component
struct Index {
  onBackPress(): boolean | void {
    return OhosSmartDialog.onBackPressed()()
  }
}

// 路由子頁(yè)面
struct JumpPage {
  build() {
    NavDestination() {
      // ....
    }
    .onBackPressed(OhosSmartDialog.onBackPressed())
  }
}
  • 如果你需要處理返回事件,在OhosSmartDialog.onBackPressed()中傳入你的方法即可
// Entry頁(yè)面處理
@Entry
@Component
struct Index {
  onBackPress(): boolean | void {
    return OhosSmartDialog.onBackPressed(this.onCustomBackPress)()
  }

  onCustomBackPress(): boolean {
    return false
  }
}

// 路由子頁(yè)面
@Component
struct JumpPage {
  build() {
    NavDestination() {
      // ...
    }
    .onBackPressed(OhosSmartDialog.onBackPressed(this.onCustomBackPress))
  }

  onCustomBackPress(): boolean {
    return false
  }
}

適配暗黑模式

  • 為了極致的體驗(yàn),深色模式切換時(shí),打開(kāi)態(tài)彈窗也應(yīng)刷新為對(duì)應(yīng)模式的樣式,故需要進(jìn)行下述配置
export default class EntryAbility extends UIAbility {  
  onConfigurationUpdate(newConfig: Configuration): void {  
    OhosSmartDialog.onConfigurationUpdate(newConfig)  
  }  
}

SmartConfig

  • 支持全局配置彈窗的默認(rèn)屬性
function init() {
  // show
  SmartDialog.config.custom.maskColor = "#75000000"
  SmartDialog.config.custom.alignment = Alignment.Center

  // showAttach
  SmartDialog.config.attach.attachAlignmentType = SmartAttachAlignmentType.center
}
  • 檢查彈窗是否存在
// 檢查當(dāng)前是否有CustomDialog,AttachDialog或LoadingDialog處于打開(kāi)狀態(tài)
let isExist = SmartDialog.checkExist()

// 檢查當(dāng)前是否有AttachDialog處于打開(kāi)狀態(tài)
let isExist = SmartDialog.checkExist({ dialogTypes: [SmartAllDialogType.attach] })

// 檢查當(dāng)前是否有tag為“xxx”的dialog處于打開(kāi)狀態(tài)
let isExist = SmartDialog.checkExist({ tag: "xxx" })

配置全局默認(rèn)樣式

  • ShowLoading 自定樣式十分簡(jiǎn)單
SmartDialog.showLoading({ builder: customLoading })

但是對(duì)于大家來(lái)說(shuō),肯定是想用 SmartDialog.showLoading() 這種簡(jiǎn)單寫(xiě)法,所以支持自定義全局默認(rèn)樣式

  • 需要在 OhosSmartDialog 上配置自定義的全局默認(rèn)樣式
@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)
}
  • 配置完你的自定樣式后,使用下述代碼,就會(huì)顯示你的 loading 樣式
SmartDialog.showLoading()

// 支持入?yún)ⅲ梢栽谔厥鈭?chǎng)景下靈活配置
SSmartDialog.showLoading({ builderArgs: 1 })

CustomDialog

  • 下方會(huì)共用的方法
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,
    // 支持任何類(lèi)型
    builderArgs: Math.random(),
  })
}

@Builder
function dialogArgs(args: number) {
  Text(`${args}`).fontColor(Color.White).padding(50)
    .borderRadius(12).backgroundColor(randomColor())
}

[圖片上傳失敗...(image-acc706-1723348012424)]

多位置彈窗

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

[圖片上傳失敗...(image-7a08ca-1723348012424)]

跨頁(yè)面交互

  • 正常使用,無(wú)需設(shè)置什么參數(shù)
export function customJumpPage() {
  SmartDialog.show({
    builder: dialogJumpPage,
  })
}

@Builder
function dialogJumpPage() {
  Text("JumPage")
    .fontSize(30)
    .padding(50)
    .borderRadius(12)
    .fontColor(Color.White)
    .backgroundColor(randomColor())
    .onClick(() => {
      // 跳轉(zhuǎn)頁(yè)面
    })
}

[圖片上傳失敗...(image-396753-1723348012424)]

關(guān)閉指定彈窗

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

[圖片上傳失敗...(image-bdd681-1723348012424)]

自定義遮罩

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

[圖片上傳失敗...(image-fde398-1723348012424)]

AttachDialog

默認(rèn)定位

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

[圖片上傳失敗...(image-c4ef31-1723348012424)]

多方向定位

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

[圖片上傳失敗...(image-43dc00-1723348012424)]

Loading

對(duì)于Loading而言,應(yīng)該有幾個(gè)比較明顯的特性

  • loading和dialog都存在頁(yè)面上,哪怕dialog打開(kāi),loading都應(yīng)該顯示dialog之上
  • loading應(yīng)該具有單一特性,多次打開(kāi)loading,頁(yè)面也應(yīng)該只存在一個(gè)loading
  • 刷新特性,多次打開(kāi)loading,后續(xù)打開(kāi)的loading樣式,應(yīng)該覆蓋之前打開(kāi)的loading樣式
  • loading使用頻率非常高,應(yīng)該支持強(qiáng)大的拓展和極簡(jiǎn)的使用

從上面列舉幾個(gè)特性而言,loading是一個(gè)非常特殊的dialog,所以需要針對(duì)其特性,進(jìn)行定制化的實(shí)現(xiàn)

當(dāng)然了,內(nèi)部已經(jīng)屏蔽了細(xì)節(jié),在使用上,和dialog的使用沒(méi)什么區(qū)別

默認(rèn)loading

SmartDialog.showLoading()

[圖片上傳失敗...(image-d487d2-1723348012424)]

自定義Loading

  • 點(diǎn)擊loading后,會(huì)再次打開(kāi)一個(gè)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())
}

[圖片上傳失敗...(image-a969c4-1723348012424)]

最后

鴻蒙版的SmartDialog,相信會(huì)對(duì)開(kāi)發(fā)鴻蒙的小伙伴們有一些幫助.

現(xiàn)在就業(yè)環(huán)境真是讓人頭皮發(fā)麻,現(xiàn)在的各種技術(shù)群里,看到好多人公司各種拖欠工資,各種失業(yè)半年的情況

淦,不知道還能寫(xiě)多長(zhǎng)時(shí)間代碼!

[圖片上傳失敗...(image-43416c-1723348012424)]

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容