HarmonyOS元服务开发系列教程(三):实现音乐播放和封面旋转

首先诚邀大家参加学习鸿蒙拿好礼活动,即日起,只要加入班级考取华为开发者基础/高级证书,并发表一篇技术文章,就有机会获得官方发放的精美礼品,数量有限,先到先得。幽蓝君的班级链接如下:​​https://developer.huawei.com/consumer/cn/training/classDetail/7b706ca975bd42e98b3bb51aa6b0be5a?type=1?ha_source=hmosclass&ha_sourceId=89000248​​

上一篇文章中分享了如何开发音乐元服务的页面,今天继续完善应用功能,完成音乐的播放和控制,以及音乐列表弹窗等功能。

本项目使用本地音频,音频文件和封面都存放在rawfile文件夹。

由于本项目使用V2版本的状态管理,定义变量的装饰器也发生了变化,比如定使用@Local定义歌曲列表和当前播放序号:

复制代码
private avPlayer?: media.AVPlayer = undefined;
@Local musicList:MusicClass[] = [
  new MusicClass('Never Really Easy','Stephanie Poetri','NeverReallyEasy.mp3','NeverReallyEasy.png'),
  new MusicClass('Only Lovers','Ronan Keating','onlylovers.mp3','onlylovers.png'),
  new MusicClass('Bumping Up and Down','Raffi','BumpingUpandDown.mp3','BumpingUpandDown.png')
]
@Local currentIndex:number = 0
然后读取本地文件并初始化播放器:
// 创建avPlayer实例对象
let avPlayer: media.AVPlayer = await media.createAVPlayer();
// 创建状态机变化回调函数
this.setAVPlayerCallback(avPlayer);
// 通过UIAbilityContext的resourceManager成员的getRawFd接口获取媒体资源播放地址
// 返回类型为{fd,offset,length},fd为HAP包fd地址,offset为媒体资源偏移量,length为播放长度
let context = getContext(this) as common.UIAbilityContext;
let fileDescriptor = await context.resourceManager.getRawFd(this.musicList[this.currentIndex].url);
let avFileDescriptor: media.AVFileDescriptor =
  { fd: fileDescriptor.fd, offset: fileDescriptor.offset, length: fileDescriptor.length };
this.isSeek = false; // 支持seek操作
// 为fdSrc赋值触发initialized状态机上报
avPlayer.fdSrc = avFileDescriptor;
this.avPlayer = avPlayer

Local大家可以理解为V1版本的State,区别是Local是组件内部的状态管理,不允许在外部进行初始化,并且Local能够实现深度状态检测。更多的V2版本装饰器会在项目中陆续介绍。

接下来注册播放器的回调,里面包含播放、暂停、完成等状态回调:

复制代码
avPlayer.on('timeUpdate', (seekDoneTime: number) => {

  if(this.duration > 0){
    let progress = seekDoneTime/this.duration
    this.progressNow = progress*100
  }
  this.progressTimeString = this.durationToTimeString(seekDoneTime)
})
// seek操作结果回调函数
avPlayer.on('seekDone', (seekDoneTime: number) => {
  console.info(`AVPlayer seek succeeded, seek time is ${seekDoneTime}`);
})

// 状态机变化回调函数
avPlayer.on('stateChange', async (state: string, reason: media.StateChangeReason) => {
  switch (state) {
    case 'idle': // 成功调用reset接口后触发该状态机上报
      console.info('AVPlayer state idle called.');
      avPlayer.release(); // 调用release接口销毁实例对象
      break;
    case 'initialized': // avplayer 设置播放源后触发该状态上报
      console.info('AVPlayer state initialized called.');
      avPlayer.prepare();
      break;
    case 'prepared': // prepare调用成功后上报该状态机
      console.info('AVPlayer state prepared called.');
      if(this.isPlay){
        avPlayer.play();
      }
      this.duration = avPlayer.duration
      this.durationTimeString = this.durationToTimeString(this.duration)
      console.log('duration:',avPlayer.duration)

      break;
    case 'playing': // play成功调用后触发该状态机上报
      console.info('AVPlayer state playing called.');
      if (this.count !== 0) {
        if (this.isSeek) {
          console.info('AVPlayer start to seek.');
          avPlayer.seek(avPlayer.duration); //seek到音频末尾
        } else {
          // 当播放模式不支持seek操作时继续播放到结尾
          console.info('AVPlayer wait to play end.');
        }
      } else {
        // avPlayer.pause(); // 调用暂停接口暂停播放
      }
      this.count++;
      break;
    case 'paused': // pause成功调用后触发该状态机上报
      console.info('AVPlayer state paused called.');
      // avPlayer.play(); // 再次播放接口开始播放
      break;
    case 'completed': // 播放结束后触发该状态机上报
      console.info('AVPlayer state completed called.');
      // avPlayer.stop(); //调用播放结束接口
      // this.endRotate();
      if(this.currentIndex < this.musicList.length - 1){
        this.currentIndex += 1
        this.changeSong()
      }else {
        this.endRotate();
      }
      break;
    case 'stopped': // stop接口成功调用后触发该状态机上报
      console.info('AVPlayer state stopped called.');
      // avPlayer.reset(); // 调用reset接口初始化avplayer状态
      break;
    case 'released':
      console.info('AVPlayer state released called.');
      break;
    default:
      console.info('AVPlayer state unknown called.');
      break;
  }
})

然后可以调用播放器的play方法进行音乐播放:

复制代码
this.avPlayer.play()

音乐播放的同时封面图片也随之转动,这里使用计时器不断修改图片角度来实现,首先定义计时器和角度变量:

复制代码
private timer?: number;
@Local rotateAngle:number=0;

实现计时器并修改角度:

复制代码
this.timer = setInterval(() => {
// 保留2位小数
this.rotateAngle = this.rotateAngle + 0.005

}, 30);

今天的内容就是这样,感谢阅读。

相关推荐
梦想不只是梦与想24 分钟前
鸿蒙三方库源码调试
harmonyos·断点调试·三方源码调试
用户117910488332 小时前
AI输出满屏井号星号?hmharness把Markdown渲染成终端表格 GitHub:swsgbl/hmharness
ai编程·harmonyos
了一1234 小时前
鸿蒙ArkWeb 长截图(滚动截图)技术方案总结
harmonyos
熊猫钓鱼>_>5 小时前
平行视界兼容模式与魔法变量配置避坑【鸿蒙心迹】
运维·服务器·开发语言·笔记·华为·harmonyos·openharmony
李&@杰6 小时前
小型园区网从零到精通
网络·tcp/ip·华为
罗四强6 小时前
第2篇:环境搭建:DevEco Studio 安装 + 第一个 Hello World
harmonyos·鸿蒙·鸿蒙系统
用户117910488337 小时前
AI终端里复制不了代码?hmharness把鼠标还给终端 GitHub:swsgbl/hmharness
ai编程·harmonyos
贾伟康9 小时前
HarmonyOS 端侧智能体长对话压缩:摘要版本、证据索引与安全回退
harmonyos·arkts·状态恢复·上下文压缩·端侧智能体
NJPJI_Yang10 小时前
鸿蒙操作系统小艺 Skill 开发实战:让应用被 HarmonyOS 7 一句话召唤
pytorch·深度学习·harmonyos
redtank200011 小时前
纯血鸿蒙学习效率三件套实测:背单词艾宾浩斯曲线、时间记录与睡眠辅助
学习·华为·harmonyos