鸿蒙开发融云demo录制语音消息

鸿蒙开发融云demo录制语音消息

融云鸿蒙版是不带UI的,得自己一步步搭建。录制语音,长按录制效果是最难弄的。还有录制声音的声音大小波动。

这次来讲如何长按录制语音消息

一、思路:

自定义TouchEvent,然后调用融云的录制语音方法。声音大小用AudioRecorder.getInstance().avRecorder?.getAudioCapturerMaxAmplitude

二、效果图:
三、关键代码:
typescript 复制代码
// 播放语音的播放器
  // 区分语音还是文字输入状态
  @State isVoiceInput:boolean = false
  microphonePermissions: Permissions = 'ohos.permission.MICROPHONE';
  hasMicrophonePermissions:boolean = false
  // 是否正在请求麦克风权限
  isRequestMicrophonePermissions:boolean = false
  // Column高度最大值,用于长按是声音的振幅
  @State yMax: number = 0;
  // Column高度最小值
  @State yMin: number = 0;
  // 声音文件名称,不能重复,否则播放自己本地语音,只会播放最后一条
  voiceName:string = ''
  // 当前录音时间,用于录制到50s后倒计时
  currentRecordVoiceTime:number = 0
  @State countDownVoiceText:string = ''
  maxMicVolumeCountId: number = 0
  // 开始录制时的时间戳
  audioTapTs: number = 0;
  audioFile: fs.File | null = null;
  // 创建音频录制与播放实例
  avPlayer?:media.AVPlayer
  @State voicePlayState: AnimationStatus = AnimationStatus.Initial
  @State voicePlayMessageId:number = 0
  // 按住说话 录音模态
  @State
  showTalkContainer: boolean = false
  // 长按状态
  @State
  pressCancelVoicePostText: PressCancelVoicePostText = PressCancelVoicePostText.none
  // "x "的坐标
  xScreenOffset: ScreenOffset = {
    x: 0,
    y: 0,
    width: 0,
    height: 0
  }

  // 按住说话 持续触发
  onPressTalk = async (event: TouchEvent) => {
    if (event.type === TouchType.Down) {
      this.currentRecordVoiceTime = 0
      this.countDownVoiceText = ''
      PermissionUtil.checkPermissions(this.microphonePermissions).then((result:boolean) => {
        if (result) {
          // 有权限
          this.hasMicrophonePermissions = true
          this.isRequestMicrophonePermissions = false
          // 手指按下时触发
          this.pressCancelVoicePostText = PressCancelVoicePostText.presssing
          // 按下
          this.showTalkContainer = true
          //  开始录音
          this.startRecordVoice()
        } else {
          this.isRequestMicrophonePermissions = true
          // 问麦克风权限
          PermissionUtil.requestPermissionsEasy(this.microphonePermissions).then((result:boolean)=>{
            if (result) {
              // 有权限
              this.hasMicrophonePermissions = true
              // 这里先不录,因为用户放开手去点击允许权限了
              //this.startRecordVoice()
            } else {
              this.hasMicrophonePermissions = false
            }
          })
        }
      })
      

    } else if (event.type === TouchType.Move) {
      if (this.hasMicrophonePermissions && !this.isRequestMicrophonePermissions) {
        // 手指移动时持续触发
        this.pressCancelVoicePostText = PressCancelVoicePostText.presssing
        // 获取当前手指的坐标
        const x = event.touches[0].displayX
        const y = event.touches[0].displayY
        // 判断是否碰到了 "X"
        let isTouchX = this.xScreenOffset.x <= x && this.xScreenOffset.x + this.xScreenOffset.width >= x &&
          this.xScreenOffset.y <= y && this.xScreenOffset.y + this.xScreenOffset.width >= y

        if (isTouchX) {
          // 取消发送
          this.pressCancelVoicePostText = PressCancelVoicePostText.cancelVoice
        }
      }

    } else if (event.type === TouchType.Up) {
      // 松开手
      if (this.showTalkContainer) {
        this.showTalkContainer = false
        clearInterval(this.maxMicVolumeCountId)
        animateTo({ duration: 0 }, () => {
          this.yMax = 0
          this.yMin = 0
        })
        if (this.hasMicrophonePermissions && !this.isRequestMicrophonePermissions) {
          if (this.pressCancelVoicePostText === PressCancelVoicePostText.cancelVoice) {
            // 取消发送
            AudioRecorder.getInstance().stopRecordingProcess()
          } else {
            // 发送录音
            this.recordAudioEndAndSendMessage()
          }
        }
      }
    }
  }
typescript 复制代码
// 正在说话 页面布局
  @Builder
  TalkContainerBuilder() {
    Column() {
      //   1 中心的提示   显示波浪线
      Column() {
          // 声纹
        ButtonWithWaterRipples({ yMax: this.yMax, yMin: this.yMin })
        if (this.countDownVoiceText){
          Text(`${this.countDownVoiceText}"后将停止录音`)
            .fontColor($r('app.color.color_white'))
            .margin({
              top:10
            })
        }
      }
      .height(80)
      .width("50%")
      .backgroundColor(this.pressCancelVoicePostText === PressCancelVoicePostText.cancelVoice ? Color.Red : "#95EC6A")
      .position({
        top: "40%",
        left: "50%"
      })
      .translate({
        x: "-50%"
      })
      .borderRadius(10)
      .justifyContent(FlexAlign.Center)
      .alignItems(HorizontalAlign.Center)

      //   2 取消和转文字
      Column({space:30}) {

        // 3  松开发送
        Text(this.pressCancelVoicePostText === PressCancelVoicePostText.cancelVoice ? '取消发送' : "松开发送")
          .fontColor("#fff")
          .width("100%")
          .textAlign(TextAlign.Center)

          Text("X")
            .fontSize(20)
            .width(60)
            .height(60)
            .borderRadius(30)
            .fontColor(this.pressCancelVoicePostText === PressCancelVoicePostText.cancelVoice ? "#000" : "#ccc")
            .backgroundColor(this.pressCancelVoicePostText === PressCancelVoicePostText.cancelVoice ? "#fff" : "#333")
            .textAlign(TextAlign.Center)
            .align(Alignment.Center)
            .fontColor("#ccc")
            .id("aabb")
            .onAppear(() => {
              let modePosition: componentUtils.ComponentInfo = componentUtils.getRectangleById("aabb");
              this.xScreenOffset.x = px2vp(modePosition.screenOffset.x)
              this.xScreenOffset.y = px2vp(modePosition.screenOffset.y)
              this.xScreenOffset.width = px2vp(modePosition.size.width)
              this.xScreenOffset.height = px2vp(modePosition.size.height)
            })
      }
      .width("100%")
      .position({
        bottom: "23%"
      })
      .padding({
        left: 60, right: 60
      })

      //   4 底部白色大球
      Row() {

      }
      .width(600)
      .height(600)
      .backgroundColor("#fff")
      .position({
        bottom: 0,
        left: "50%"
      })
      .translate({
        x: "-50%",
        y: "70%"
      })
      .borderRadius("50%")

    }
    .width("100%")
    .height("100%")
    .backgroundColor("rgba(0,0,0,0.5)")
  }
四、鸿蒙融云Demo源码结构图:

有问题或者需要完整源码demo的私信我

相关推荐
博览鸿蒙28 分钟前
鸿蒙操作系统(HarmonyOS)的应用开发入门
华为·harmonyos
Damon小智7 小时前
HarmonyOS NEXT 技术实践-基于基础视觉服务的多目标识别
华为·harmonyos
爱笑的眼睛111 天前
uniapp 极速上手鸿蒙开发
华为·uni-app·harmonyos
K.P1 天前
鸿蒙元服务从0到上架【第三篇】(第二招有捷径)
华为·harmonyos·鸿蒙系统
K.P1 天前
鸿蒙元服务从0到上架【第二篇】
华为·harmonyos·鸿蒙系统
敲代码的小强1 天前
Flutter项目兼容鸿蒙Next系统
flutter·华为·harmonyos
程序猿会指北1 天前
纯血鸿蒙APP实战开发——Text实现部分文本高亮和超链接样式
移动开发·harmonyos·arkts·openharmony·arkui·组件化·鸿蒙开发
鸿蒙自习室1 天前
鸿蒙开发——关系型数据库的基本使用与跨设备同步
前端·数据库·华为·harmonyos·鸿蒙
SoraLuna2 天前
「Mac畅玩鸿蒙与硬件45」UI互动应用篇22 - 评分统计工具
开发语言·macos·ui·华为·harmonyos
资讯分享周2 天前
鸿蒙风起,未来已来——云学堂鸿蒙应用认证开营啦!
华为·harmonyos