鴻蒙專案實戰(三):自定義彈窗開發實踐

听着music睡發表於2024-11-09

自定義彈窗選型

合理選擇不同的系統能力實現彈窗,有利於提升應用開發效率,實現更好的功能需求,因此瞭解自定義彈窗的選型和差異非常重要。在應用開發中,為了選擇出合適的彈窗選型,從使用場景上,需要重點關注以下兩點:

  • 彈窗與介面程式碼解耦

    在開發業務邏輯時,例如遇到一些網路請求失敗的場景,需要觸發相應的彈窗提醒使用者進行操作,由於在任何頁面都有可能觸發對應的彈窗,此時彈窗不是與某個頁面相關聯,這個情況下,就需要彈窗與介面的解耦。

  • 彈窗在介面跳轉後保留

    在一些許可權配置頁,使用者首次進入應用時會彈出許可權配置彈窗,讓使用者進行操作,此時若點選跳轉到隱私詳情頁面,返回後彈窗需要保留在頁面上。

系統提供了四種不同的方式來實現自定義彈窗,分別是CustomDialog、promptAction、UIContext.getPromptAction、Navigation.Dialog,在開發業務時,需要結合每種彈窗的特點來選擇彈窗。

  • CustomDialog彈窗,必須在@Component struct內部定義,即在UI層建立控制器,當一個頁面需要彈出多個自定義彈窗時,就需要建立對應個數的CustomDialogController,這會造成UI層程式碼冗餘,無法做到彈窗與介面的解耦。
  • promptAction彈窗,為了解決CustomDialog彈窗的問題,支援了UI元素複用機制@Builder,但依賴UI元件
  • UIContext.getPromptAction彈窗,基於promptAction彈窗演進而來,支援全域性自定義彈窗,不依賴UI元件,依賴UIContext,支援在非頁面檔案中使用,彈窗內容支援動態修改,支援自定義彈窗圓角半徑、大小和位置,適合在與頁面解耦的全域性彈窗、自定義彈窗顯示和退出動畫等場景下使用。
  • Navigation.Dialog彈窗,基於Navigation路由形式,以Component元件頁面存在於路由棧中,以進出棧的方式開啟或關閉彈窗,可以實現彈窗與UI介面解耦,預設透明顯示,適合在切換頁面彈窗不消失場景下使用。

總結如下:

使用示例如下:

一、CustomerDialog

1、定義:

// 自定義提示彈框
// @CustomerDialog
@CustomDialog
export struct TipDialog {
  @State title: string = "提示" // 標題
  @State message: string = ""; // 提示內容
  @State cancelValue: string = "取消"; //取消按鈕文案
  @State confirmValue: string = "確定"; //確定按鈕文案
  cancel?: () => void // 取消事件
  confirm?: () => void // 確定事件
  controller: CustomDialogController = new CustomDialogController({
    builder: TipDialog()
  })

  build() {
    Column() {
      Text(this.title)
        .width('100%')
        .height(40)
        .textAlign(TextAlign.Center)
        .fontColor(Color.White)
        .backgroundColor($r('app.color.dialog_title_bg'))
        .borderRadius({ topLeft: 10, topRight: 10 })
      Text(this.message)
        .height(70)
        .width('100%')
        .textAlign(TextAlign.Center)
        .backgroundColor(Color.White)
      Row() {
        Text(this.cancelValue)
          .layoutWeight(1)
          .backgroundColor(Color.White)
          .fontColor(Color.Black)
          .textAlign(TextAlign.Center)
          .height(40)
          .borderRadius({ bottomLeft: 10 })
          .onClick(() => {
            if (this.cancel) {
              this.cancel()
            }
          })
        Text(this.confirmValue)
          .layoutWeight(1)
          .backgroundColor($r('app.color.main_color'))
          .fontColor(Color.White)
          .textAlign(TextAlign.Center)
          .height(40)
          .borderRadius({ bottomRight: 10 })
          .onClick(() => {
            if (this.confirm) {
              this.confirm()
            }
          })
      }
    }.width('100%')
  }
}

2、使用

import {TipDialog} from '../../components/dialogs/TipDialog'

@Entry
@Component
struct More {
  // 有多少個彈框,就需要定義多少個CustomDialogController
  // 造成UI層程式碼冗餘,無法做到彈窗與介面的解耦。
  controller:CustomDialogController= new CustomDialogController({
    builder : TipDialog({
      message: '點選了內容',
      confirm: ()=>{
        console.log('點選了確定')
        this.controller.close()
      },
      cancel: ()=>{
        console.log('點選了取消')
        this.controller.close()
      }
    }),
    alignment: DialogAlignment.Center,
    offset: { dx: 0, dy: -20 },
    customStyle: false,
    width:'80%',
    cornerRadius:10,
    backgroundColor: 0xd9ffffff,
  })

  showDialog(){
    this.controller.open()
  }
  build() {
    Row() {
      Column() {
        Button('開啟彈框')
          .fontWeight(FontWeight.Bold)
          .onClick(()=>{
            this.showDialog()
          })
      }
      .width('100%')
    }
    .height('100%')
  }
}

二、UIContext.getPromptAction彈窗

1、定義

// 自定義彈框元件
// 使用UIContext.getPromptAction.openCustomDialog的方式
// https://developer.huawei.com/consumer/cn/doc/best-practices-V5/bpta-custome-dialog-development-practice-V5#section7466312192919
// https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/js-apis-arkui-uicontext-V5#opencustomdialog12

// 彈框可設定引數
export class DialogParms{
  public title: string = "提示" // 標題
  public message: string = "" // 提示內容
  public cancelValue: string = "取消" // 取消按鈕文案
  public confirmValue: string = "確定" // 確定按鈕文案
  public cancel?: (() => void) | undefined   // 取消事件
  public confirm?: (() => void) | undefined   // 確定事件
  public setTitle(value: string):void {
    this.title = value
  }
  public setMessage(value: string):void {
    this.message = value
  }
  public setCancelValue(value: string):void {
    this.cancelValue = value
  }
  public setConfirmValue(value: string):void {
    this.confirmValue = value
  }

  public setCancel(value: (() => void) | undefined){
    this.cancel = value
  }

  public setConfirm(value: (() => void) | undefined) {
    this.confirm = value
  }

  constructor(title:string) {
    this.title = title
  }


}

// 全域性自定義函式
@Builder
export function  WarmDialog(parms:DialogParms){
  Column() {
    Text(parms.title)
      .width('100%')
      .height(40)
      .textAlign(TextAlign.Center)
      .fontColor(Color.White)
      .backgroundColor($r('app.color.dialog_title_bg'))
      .borderRadius({topLeft:10,topRight:10})
    Text(parms.message)
      .height(70)
      .width('100%')
      .textAlign(TextAlign.Center)
      .backgroundColor(Color.White)
    Row() {
      Text(parms.cancelValue)
        .layoutWeight(1)
        .backgroundColor(Color.White)
        .fontColor(Color.Black)
        .textAlign(TextAlign.Center)
        .height(40)
        .borderRadius({bottomLeft:10})
        .onClick(()=>{
          if (parms.cancel) {
            parms.cancel()
          }
        })
      Text(parms.confirmValue)
        .layoutWeight(1)
        .backgroundColor($r('app.color.main_color'))
        .fontColor(Color.White)
        .textAlign(TextAlign.Center)
        .height(40)
        .borderRadius({bottomRight:10})
        .onClick(()=>{
          if (parms.confirm) {
            parms.confirm()
          }
        })
    }
  }.width('80%')
}

2、使用

import { ComponentContent, PromptAction, UIContext } from '@kit.ArkUI';
import {WarmDialog,DialogParms} from '../../components/dialogs/WarmDialog'
@Entry
@Component
struct More {
  uiContext:UIContext = this.getUIContext()

  showDialog(){
    let promptAction:PromptAction = this.uiContext.getPromptAction()
    let dialogParms = new DialogParms("警告")
    dialogParms.setTitle("警告")
    dialogParms.setMessage("這是警告內容")
    dialogParms.setCancel(()=>{
      promptAction.closeCustomDialog(builderView)
      console.log('點選了取消')
    })
    dialogParms.setConfirm(()=>{
      promptAction.closeCustomDialog(builderView)
      console.log('點選了確定')
    })
    let builderView = new ComponentContent(
      this.uiContext,
      wrapBuilder(WarmDialog),dialogParms
    )
    promptAction.openCustomDialog(builderView,{alignment:DialogAlignment.Center},)
  }
  build() {
    Row() {
      Column() {
        Button('開啟彈框')
          .fontWeight(FontWeight.Bold)
          .onClick(()=>{
            this.showDialog()
          })
      }
      .width('100%')
    }
    .height('100%')
  }
}

相關文章