HarmonyOS应用通知与弹窗:从Toast到自定义Dialog

通知和弹窗是应用与用户交互的重要方式,HarmonyOS提供了丰富的通知机制和弹窗组件。本文将全面讲解从简单的Toast提示到复杂的自定义Dialog的实现,帮助您构建良好的用户交互体验。

一、Toast提示消息

1.1 Toast基础概念

Toast是一种轻量级的消息提示机制,它会在屏幕底部短暂显示后自动消失,不会打断用户操作。

特性分析:

  • 非阻塞式:不中断用户当前操作
  • 自动消失:显示2-3秒后自动关闭
  • 位置固定:通常显示在屏幕底部
  • 样式简单:文字提示,不支持复杂交互

1.2 基础Toast使用

import prompt from '@ohos.prompt'

@Component
struct ToastExamples {
  build() {
    Column({ space: 15 }) {
      Text('Toast提示示例')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
      
      Button('基础Toast')
        .onClick(() => {
          prompt.showToast({
            message: '这是一个基础Toast提示',
            duration: 2000, // 显示时长(毫秒)
            bottom: 100     // 距离底部距离
          })
        })
      
      Button('长文本Toast')
        .onClick(() => {
          prompt.showToast({
            message: '这是一个比较长的Toast提示消息,会自动换行显示',
            duration: 3000
          })
        })
      
      Button('带图标Toast')
        .onClick(() => {
          prompt.showToast({
            message: '操作成功',
            duration: 2000
          })
        })
    }
    .width('100%')
    .padding(20)
  }
}

1.3 Toast高级应用

class ToastManager {
  // 显示成功提示
  static showSuccess(message: string): void {
    prompt.showToast({
      message: message,
      duration: 2000,
      bottom: 150
    })
  }
  
  // 显示错误提示
  static showError(message: string): void {
    prompt.showToast({
      message: message,
      duration: 2500,
      bottom: 150
    })
  }
  
  // 显示警告提示
  static showWarning(message: string): void {
    prompt.showToast({
      message: message,
      duration: 2000,
      bottom: 150
    })
  }
  
  // 显示加载中提示
  static showLoading(message: string = '加载中...'): void {
    prompt.showToast({
      message: message,
      duration: 0, // 0表示不自动关闭
      bottom: 150
    })
  }
  
  // 关闭Toast
  static hideToast(): void {
    prompt.hideToast()
  }
}

// 使用示例
ToastManager.showSuccess('数据保存成功')
ToastManager.showError('网络连接失败')

二、ActionSheet操作菜单

2.1 ActionSheet基础使用

ActionSheet是从屏幕底部滑出的操作菜单,用于提供多个相关操作选项。

@Component
struct ActionSheetExamples {
  build() {
    Column({ space: 15 }) {
      Text('ActionSheet示例')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
      
      Button('显示操作菜单')
        .onClick(() => {
          this.showImageActionSheet()
        })
    }
    .width('100%')
    .padding(20)
  }

  // 图片操作菜单
  private showImageActionSheet(): void {
    const buttons = [
      {
        text: '拍照',
        color: '#007DFF'
      },
      {
        text: '从相册选择', 
        color: '#007DFF'
      },
      {
        text: '查看大图',
        color: '#007DFF'
      }
    ]
    
    prompt.showActionMenu({
      title: '图片操作',
      buttons: buttons,
      success: (index: number) => {
        this.handleImageAction(index)
      },
      cancel: () => {
        console.log('用户取消了操作')
      }
    })
  }

  private handleImageAction(index: number): void {
    switch (index) {
      case 0:
        this.takePhoto()
        break
      case 1:
        this.pickFromGallery()
        break
      case 2:
        this.viewLargeImage()
        break
    }
  }

  private takePhoto(): void {
    ToastManager.showSuccess('打开相机')
  }

  private pickFromGallery(): void {
    ToastManager.showSuccess('打开相册')
  }

  private viewLargeImage(): void {
    ToastManager.showSuccess('查看大图')
  }
}

2.2 带图标的ActionSheet

private showAdvancedActionSheet(): void {
  const buttons = [
    {
      text: '微信分享',
      color: '#07C160',
      icon: $r('app.media.wechat_icon')
    },
    {
      text: '朋友圈分享',
      color: '#07C160', 
      icon: $r('app.media.moment_icon')
    },
    {
      text: 'QQ分享',
      color: '#12B7F5',
      icon: $r('app.media.qq_icon')
    },
    {
      text: '微博分享',
      color: '#E6162D',
      icon: $r('app.media.weibo_icon')
    }
  ]
  
  prompt.showActionMenu({
    title: '分享到',
    buttons: buttons,
    success: (index: number) => {
      this.handleShareAction(index)
    }
  })
}

三、Dialog对话框

3.1 系统标准Dialog

HarmonyOS提供了多种标准对话框,适用于常见的交互场景。

@Component
struct SystemDialogExamples {
  @State userName: string = ''

  build() {
    Column({ space: 15 }) {
      Text('系统对话框示例')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
      
      Button('确认对话框')
        .onClick(() => {
          this.showConfirmDialog()
        })
      
      Button('输入对话框')  
        .onClick(() => {
          this.showInputDialog()
        })
      
      Button('单选对话框')
        .onClick(() => {
          this.showSingleChoiceDialog()
        })
      
      Button('多选对话框')
        .onClick(() => {
          this.showMultiChoiceDialog()
        })
      
      Text(`用户名: ${this.userName}`)
        .fontSize(14)
        .margin({ top: 20 })
    }
    .width('100%')
    .padding(20)
  }

  // 确认对话框
  private showConfirmDialog(): void {
    prompt.showDialog({
      title: '确认删除',
      message: '您确定要删除这条记录吗?此操作不可撤销。',
      buttons: [
        {
          text: '取消',
          color: '#666666'
        },
        {
          text: '确定删除',
          color: '#FF3B30'
        }
      ],
      success: (index: number) => {
        if (index === 1) {
          this.deleteRecord()
        }
      }
    })
  }

  // 输入对话框
  private showInputDialog(): void {
    prompt.showDialog({
      title: '输入用户名',
      message: '请输入您的用户名:',
      autoClose: false, // 不自动关闭
      editors: [
        {
          type: prompt.InputType.TEXT,
          text: this.userName,
          placeholder: '请输入用户名'
        }
      ],
      buttons: [
        {
          text: '取消'
        },
        {
          text: '确定'
        }
      ],
      success: (index: number, value: string) => {
        if (index === 1) {
          this.userName = value
          ToastManager.showSuccess(`用户名设置为: ${value}`)
        }
      }
    })
  }

  // 单选对话框
  private showSingleChoiceDialog(): void {
    const options = ['选项一', '选项二', '选项三']
    
    prompt.showDialog({
      title: '单选选择',
      message: '请选择一个选项:',
      editors: [
        {
          type: prompt.InputType.RADIO,
          options: options,
          selected: 0 // 默认选中第一个
        }
      ],
      buttons: [
        {
          text: '取消'
        },
        {
          text: '确定'  
        }
      ],
      success: (index: number, value: number) => {
        if (index === 1) {
          ToastManager.showSuccess(`选择了: ${options[value]}`)
        }
      }
    })
  }

  // 多选对话框
  private showMultiChoiceDialog(): void {
    const options = ['音乐', '体育', '读书', '旅游', '游戏']
    
    prompt.showDialog({
      title: '兴趣爱好',
      message: '请选择您的兴趣爱好:',
      editors: [
        {
          type: prompt.InputType.CHECKBOX,
          options: options,
          selected: [0, 2] // 默认选中项
        }
      ],
      buttons: [
        {
          text: '取消'
        },
        {
          text: '确定'
        }
      ],
      success: (index: number, value: number[]) => {
        if (index === 1) {
          const selected = value.map(i => options[i]).join(', ')
          ToastManager.showSuccess(`选择了: ${selected}`)
        }
      }
    })
  }

  private deleteRecord(): void {
    ToastManager.showSuccess('记录删除成功')
  }
}

四、自定义Dialog实现

4.1 基础自定义Dialog

当系统对话框无法满足需求时,可以创建完全自定义的Dialog。

@Component
struct CustomDialogExample {
  @State isDialogVisible: boolean = false
  @State dialogType: string = 'info'

  build() {
    Column({ space: 20 }) {
      Text('自定义Dialog示例')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
      
      Row({ space: 15 }) {
        Button('信息对话框')
          .onClick(() => {
            this.dialogType = 'info'
            this.isDialogVisible = true
          })
        
        Button('警告对话框')
          .onClick(() => {
            this.dialogType = 'warning'  
            this.isDialogVisible = true
          })
        
        Button('输入对话框')
          .onClick(() => {
            this.dialogType = 'input'
            this.isDialogVisible = true
          })
      }
      
      // 自定义Dialog
      if (this.isDialogVisible) {
        this.buildCustomDialog()
      }
    }
    .width('100%')
    .padding(20)
  }

  @Builder
  buildCustomDialog() {
    // 半透明背景层
    Stack({ alignContent: Alignment.Center }) {
      // 背景遮罩
      Rectangle()
        .width('100%')
        .height('100%')
        .backgroundColor('#000000')
        .opacity(0.5)
        .onClick(() => {
          this.isDialogVisible = false
        })
      
      // 对话框内容
      Column({ space: 0 }) {
        this.buildDialogHeader()
        this.buildDialogContent()
        this.buildDialogActions()
      }
      .width('80%')
      .backgroundColor(Color.White)
      .borderRadius(12)
      .shadow({ radius: 24, color: '#40000000' })
    }
    .width('100%')
    .height('100%')
    .position({ x: 0, y: 0 })
  }

  @Builder
  buildDialogHeader() {
    let title = ''
    let icon = $r('app.media.info_icon')
    let bgColor = '#007DFF'
    
    switch (this.dialogType) {
      case 'warning':
        title = '警告'
        icon = $r('app.media.warning_icon')
        bgColor = '#FF9500'
        break
      case 'input':
        title = '输入信息'
        icon = $r('app.media.edit_icon')  
        bgColor = '#34C759'
        break
      default:
        title = '信息'
    }
    
    Row({ space: 10 }) {
      Image(icon)
        .width(20)
        .height(20)
      
      Text(title)
        .fontSize(18)
        .fontWeight(FontWeight.Bold)
        .fontColor(Color.White)
    }
    .width('100%')
    .padding(20)
    .backgroundColor(bgColor)
    .borderRadius({ topLeft: 12, topRight: 12 })
  }

  @Builder
  buildDialogContent() {
    Column({ space: 15 }) {
      if (this.dialogType === 'input') {
        this.buildInputContent()
      } else {
        this.buildMessageContent()
      }
    }
    .width('100%')
    .padding(20)
  }

  @Builder
  buildInputContent() {
    Column({ space: 10 }) {
      Text('请输入您的信息:')
        .fontSize(14)
        .fontColor('#666')
      
      TextInput({ placeholder: '请输入内容' })
        .width('100%')
        .height(40)
        .backgroundColor('#F5F5F5')
        .padding(10)
        .borderRadius(6)
    }
  }

  @Builder
  buildMessageContent() {
    Text(this.dialogType === 'warning' ? 
      '这是一个警告消息,请注意相关操作的风险。' : 
      '这是一个普通的信息提示消息。'
    )
    .fontSize(14)
    .fontColor('#333')
    .textAlign(TextAlign.Center)
  }

  @Builder
  buildDialogActions() {
    Row({ space: 10 }) {
      if (this.dialogType === 'warning') {
        Button('取消')
          .layoutWeight(1)
          .backgroundColor('#666666')
          .fontColor(Color.White)
          .onClick(() => {
            this.isDialogVisible = false
          })
      }
      
      Button(this.dialogType === 'input' ? '确定' : '我知道了')
        .layoutWeight(1)
        .backgroundColor('#007DFF')
        .fontColor(Color.White)
        .onClick(() => {
          this.handleDialogConfirm()
        })
    }
    .width('100%')
    .padding(20)
  }

  private handleDialogConfirm(): void {
    ToastManager.showSuccess('对话框操作确认')
    this.isDialogVisible = false
  }
}

4.2 高级自定义Dialog组件

创建可复用的自定义Dialog组件:

@Component
struct CustomAlertDialog {
  @Prop title: string = ''
  @Prop message: string = ''
  @Prop type: 'info' | 'warning' | 'error' | 'success' = 'info'
  @Prop visible: boolean = false
  @Prop onClose: () => void = () => {}
  @Prop onConfirm: () => void = () => {}

  build() {
    if (this.visible) {
      Stack({ alignContent: Alignment.Center }) {
        // 遮罩层
        Rectangle()
          .width('100%')
          .height('100%')
          .backgroundColor('#000000')
          .opacity(0.5)
          .onClick(() => {
            this.onClose()
          })
        
        // 对话框主体
        Column({ space: 0 }) {
          // 标题栏
          this.buildHeader()
          
          // 内容区域
          Column({ space: 15 }) {
            Text(this.message)
              .fontSize(14)
              .fontColor('#333')
              .textAlign(TextAlign.Center)
          }
          .width('100%')
          .padding(20)
          
          // 操作按钮
          Row({ space: 10 }) {
            Button('取消')
              .layoutWeight(1)
              .backgroundColor('#666666')
              .fontColor(Color.White)
              .onClick(() => {
                this.onClose()
              })
            
            Button('确定')
              .layoutWeight(1)
              .backgroundColor(this.getConfirmButtonColor())
              .fontColor(Color.White)
              .onClick(() => {
                this.onConfirm()
              })
          }
          .width('100%')
          .padding(20)
        }
        .width('80%')
        .backgroundColor(Color.White)
        .borderRadius(12)
      }
      .width('100%')
      .height('100%')
      .position({ x: 0, y: 0 })
    }
  }

  @Builder
  buildHeader() {
    let bgColor = '#007DFF'
    let icon = $r('app.media.info_icon')
    
    switch (this.type) {
      case 'warning':
        bgColor = '#FF9500'
        icon = $r('app.media.warning_icon')
        break
      case 'error':
        bgColor = '#FF3B30'
        icon = $r('app.media.error_icon')
        break
      case 'success':
        bgColor = '#34C759'
        icon = $r('app.media.success_icon')
        break
    }
    
    Row({ space: 10 }) {
      Image(icon)
        .width(20)
        .height(20)
      
      Text(this.title)
        .fontSize(18)
        .fontWeight(FontWeight.Bold)
        .fontColor(Color.White)
        .layoutWeight(1)
    }
    .width('100%')
    .padding(20)
    .backgroundColor(bgColor)
    .borderRadius({ topLeft: 12, topRight: 12 })
  }

  private getConfirmButtonColor(): string {
    switch (this.type) {
      case 'warning': return '#FF9500'
      case 'error': return '#FF3B30'
      case 'success': return '#34C759'
      default: return '#007DFF'
    }
  }
}

// 使用示例
@Component
struct CustomDialogUsage {
  @State isDialogVisible: boolean = false

  build() {
    Column({ space: 15 }) {
      Button('显示自定义对话框')
        .onClick(() => {
          this.isDialogVisible = true
        })
      
      CustomAlertDialog({
        title: '操作确认',
        message: '您确定要执行此操作吗?',
        type: 'warning',
        visible: this.isDialogVisible,
        onClose: () => {
          this.isDialogVisible = false
          ToastManager.showSuccess('用户取消了操作')
        },
        onConfirm: () => {
          this.isDialogVisible = false
          ToastManager.showSuccess('操作已确认')
        }
      })
    }
  }
}

五、通知系统集成

5.1 系统通知基础

import notification from '@ohos.notification'

@Component
struct NotificationExamples {
  build() {
    Column({ space: 15 }) {
      Text('系统通知示例')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)
      
      Button('发送普通通知')
        .onClick(() => {
          this.sendBasicNotification()
        })
      
      Button('发送重要通知')
        .onClick(() => {
          this.sendImportantNotification()
        })
      
      Button('发送进度通知')
        .onClick(() => {
          this.sendProgressNotification()
        })
      
      Button('取消所有通知')
        .onClick(() => {
          this.cancelAllNotifications()
        })
    }
    .width('100%')
    .padding(20)
  }

  // 发送基础通知
  private async sendBasicNotification(): Promise<void> {
    try {
      const request: notification.NotificationRequest = {
        content: {
          contentType: notification.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
          normal: {
            title: '新消息通知',
            text: '您有一条新的消息,请及时查看。',
            additionalText: '刚刚'
          }
        },
        id: 1,
        slotType: notification.SlotType.SOCIAL_COMMUNICATION
      }
      
      await notification.publish(request)
      ToastManager.showSuccess('通知发送成功')
    } catch (error) {
      console.error('发送通知失败:', error)
      ToastManager.showError('通知发送失败')
    }
  }

  // 发送重要通知
  private async sendImportantNotification(): Promise<void> {
    const request: notification.NotificationRequest = {
      content: {
        contentType: notification.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
        normal: {
          title: '重要提醒',
          text: '系统即将进行维护,请保存好您的工作。',
          additionalText: '重要'
        }
      },
      deliveryTime: new Date().getTime(), // 立即发送
      id: 2,
      slotType: notification.SlotType.SERVICE_REMINDER,
      // 设置通知重要性
      notificationFlags: {
        sound: true,
        vibration: true
      }
    }
    
    await notification.publish(request)
  }

  // 发送进度通知
  private async sendProgressNotification(): Promise<void> {
    const request: notification.NotificationRequest = {
      content: {
        contentType: notification.ContentType.NOTIFICATION_CONTENT_PROGRESS,
        progress: {
          title: '文件下载',
          text: '正在下载文件...',
          progressValue: 50, // 50%进度
          progressMaxValue: 100
        }
      },
      id: 3,
      slotType: notification.SlotType.OTHER
    }
    
    await notification.publish(request)
  }

  // 取消所有通知
  private async cancelAllNotifications(): Promise<void> {
    await notification.cancelAll()
    ToastManager.showSuccess('所有通知已取消')
  }
}

六、最佳实践与性能优化

6.1 通知管理策略

class NotificationManager {
  private static notificationId: number = 1000
  
  // 发送消息通知
  static async sendMessageNotification(sender: string, message: string): Promise<void> {
    const request: notification.NotificationRequest = {
      content: {
        contentType: notification.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
        normal: {
          title: `新消息来自 ${sender}`,
          text: message,
          additionalText: '消息'
        }
      },
      id: this.notificationId++,
      slotType: notification.SlotType.SOCIAL_COMMUNICATION
    }
    
    await notification.publish(request)
  }
  
  // 发送系统通知
  static async sendSystemNotification(title: string, content: string): Promise<void> {
    const request: notification.NotificationRequest = {
      content: {
        contentType: notification.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
        normal: {
          title: title,
          text: content,
          additionalText: '系统'
        }
      },
      id: this.notificationId++,
      slotType: notification.SlotType.SERVICE_REMINDER
    }
    
    await notification.publish(request)
  }
  
  // 更新进度通知
  static async updateProgressNotification(id: number, progress: number, text: string): Promise<void> {
    const request: notification.NotificationRequest = {
      content: {
        contentType: notification.ContentType.NOTIFICATION_CONTENT_PROGRESS,
        progress: {
          title: '下载进度',
          text: text,
          progressValue: progress,
          progressMaxValue: 100
        }
      },
      id: id
    }
    
    await notification.publish(request)
  }
}

6.2 Dialog性能优化

@Component
struct OptimizedDialog {
  @State isVisible: boolean = false
  
  // 使用aboutToAppear进行初始化
  aboutToAppear() {
    console.log('Dialog组件初始化')
  }
  
  // 使用aboutToDisappear进行清理
  aboutToDisappear() {
    console.log('Dialog组件销毁')
  }
  
  build() {
    if (this.isVisible) {
      // 复杂的Dialog内容...
    }
  }
  
  // 显示Dialog(带动画)
  showWithAnimation() {
    this.isVisible = true
    // 可以添加显示动画
  }
  
  // 隐藏Dialog(带动画)
  hideWithAnimation() {
    // 先执行隐藏动画
    setTimeout(() => {
      this.isVisible = false
    }, 300)
  }
}

总结

通过本文的深入学习,您应该掌握了HarmonyOS通知与弹窗的完整知识体系:

  1. Toast提示:轻量级消息提示,不中断用户操作
  2. ActionSheet:底部操作菜单,提供多个相关选项
  3. 系统Dialog:标准对话框,支持确认、输入、选择等场景
  4. 自定义Dialog:完全自定义的弹窗,满足特殊UI需求
  5. 系统通知:状态栏通知,支持多种内容和优先级
  6. 最佳实践:性能优化和用户体验考虑

合理运用这些交互组件可以显著提升应用的用户体验和易用性。

需要参加鸿蒙认证的请点击 鸿蒙认证链接

posted @ 2025-10-30 11:03  ifeng918  阅读(245)  评论(0)    收藏  举报