HarmonyOS Next 中Media Kit使用说明及其封装音乐播放器及其背景音乐播放组件 原创

第一小趴菜
发布于 2024-11-28 12:40
浏览
0收藏

前言

工具帖子,有需要直接用的可以直接拿

通过Media Kit封装音乐播放器以及封装背景音乐使用组件
效果如下:
HarmonyOS Next 中Media Kit使用说明及其封装音乐播放器及其背景音乐播放组件-鸿蒙开发者社区

实现步骤

播放媒体文件的流程为:创建AVPlayer->设置播放资源->设置播放参数(音量/倍速/焦点模式)->播放控制(播放/暂停/跳转/停止)->重置->销毁资源。

我们可以通过AVPlayer的state属性主动获取当前状态或使用on(‘stateChange’)方法监听状态变化。

播放状态变化示意图如下

HarmonyOS Next 中Media Kit使用说明及其封装音乐播放器及其背景音乐播放组件-鸿蒙开发者社区HarmonyOS Next 中Media Kit使用说明及其封装音乐播放器及其背景音乐播放组件-鸿蒙开发者社区

下方例子为步骤讲解,完整代码放到最后

1.创建AVPlayer

我们通过createAVPlayer()方法用于创建我们的媒体播放组件

    if (this.avPlayer !== null) {
      this.avPlayer?.reset()
    }
    // 创建avPlayer实例对象
    this.avPlayer = await media.createAVPlayer();
    // 绑定回调函数
    this.setAVPlayerCallback(this.avPlayer);
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

同时我们需要绑定我们的回调函数,我们通过这个回调函数,在回调函数中可以获取到组件中需要的信息,比如播放时长、等内容,如下方展示

事件类型 说明
stateChange 必要事件,监听播放器的state属性改变。
error 必要事件,监听播放器的错误信息。
durationUpdate 用于进度条,监听进度条长度,刷新资源时长。
timeUpdate 用于进度条,监听进度条当前位置,刷新当前时间。
seekDone 响应API调用,监听seek()请求完成情况。当使用seek()跳转到指定播放位置后,如果seek操作成功,将上报该事件。
speedDone 响应API调用,监听setSpeed()请求完成情况。当使用setSpeed()设置播放倍速后,如果setSpeed操作成功,将上报该事件。
volumeChange 响应API调用,监听setVolume()请求完成情况。当使用setVolume()调节播放音量后,如果setVolume操作成功,将上报该事件。
bufferingUpdate 用于网络播放,监听网络播放缓冲信息,用于上报缓冲百分比以及缓存播放进度。
audioInterrupt 监听音频焦点切换信息,搭配属性audioInterruptMode使用。如果当前设备存在多个音频正在播放,音频焦点被切换(即播放其他媒体如通话等)时将上报该事件,应用可以及时处理。

添加这些事件的格式为:

avPlayer.on('事件', (参数) => {
    // 事件逻辑
      })
  • 1.
  • 2.
  • 3.

我们这里的回调函数作为样例,只选择通过在加上stateChange和error,保证能够正常播放之外,再添加timeUpdate和durationUpdate

所以我们的回调函数为下方样式

  setAVPlayerCallback(avPlayer: media.AVPlayer) {
    if (avPlayer !== null) {
      avPlayer.on('error', (err) => {
        console.error(`播放器发生错误,错误码:${err.code}, 错误信息:${err.message}`);
        avPlayer?.reset();
      });

      avPlayer.on('stateChange', async (state, reason) => {
        switch (state) {
          case 'initialized':
            console.info('资源初始化完成');
            avPlayer?.prepare();
            break;
          case 'prepared':
            console.info('资源准备完成');
            avPlayer?.play();
            break;
          case 'completed':
            console.info('播放完成');
            avPlayer?.stop();
            break;
        }
      });

      // 获取进度条长度
      avPlayer.on('timeUpdate', (time:number) => {
        this.outSetValue = time
        console.info('timeUpdate success,and new time is :' + time)

      })

      // 获取时间长度
      avPlayer.on('durationUpdate', (duration: number) => {
        this.SliderLength = duration;
        console.info('durationUpdate success,new duration is :' + duration)
      })
    }
  }
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.

可以根据自己的需要进行添加

2.设置播放资源

我们播放音乐肯定要设置播放资源,我把音乐名称,作者和文件都封装成一个类中,如下方展示

interface song_item{
  // 歌曲名称
  Title:string
  // 作者
  Author:string
  // 歌曲文件
  SongFile:string
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.

首先我们需要把mp3文件放到rawfile目录下

HarmonyOS Next 中Media Kit使用说明及其封装音乐播放器及其背景音乐播放组件-鸿蒙开发者社区HarmonyOS Next 中Media Kit使用说明及其封装音乐播放器及其背景音乐播放组件-鸿蒙开发者社区

我们将歌曲列表封装好

  aboutToAppear(): void {
    this.songs.push({Title:"花日",Author:"CMJ",SongFile:"CMJ.mp3"});
    this.songs.push({Title:"踊り子",Author:"Vaundy",SongFile:"Vaundy.mp3"});
  }

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.

并将歌曲文件地址装入组件

    let context = getContext(this) as common.UIAbilityContext;
    let fileDescriptor = await context.resourceManager.getRawFd(item.SongFile);
    let avFileDescriptor: media.AVFileDescriptor =
      { fd: fileDescriptor.fd, offset: fileDescriptor.offset, length: fileDescriptor.length };
    // this.isSeek = true; // 支持seek操作
    // 为fdSrc赋值触发initialized状态机上报
    this.avPlayer.fdSrc = avFileDescriptor;
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

就可以对播放控制进行封装了

3.播放控制

我们通过timeUpdate和durationUpdate,可以获取到播放时长信息,将信息封装入进度条组件

          // 进度条

          Row() {
            Slider({
              value: this.outSetValue,
              min: 0,
              max: this.SliderLength,
              step: 1,
              style: SliderStyle.OutSet
            })
              .width("80%")
              .blockColor('#FFFFFF')
              .trackColor('#182431')
              .selectedColor('#cbf1f5')
              .showSteps(true)
              .showTips(false)
              .onChange((value: number, mode: SliderChangeMode) => {
                this.outSetValue = value
                // 调节长度
                this.avPlayer?.seek(this.outSetValue)
              })
            Text(this.invertTime(this.outSetValue)).fontSize(16)
          }
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.

因为我们通过回调函数获得的时长为毫秒

这个时候我们要将时长转化为 hh:mm:ss的格式,就像这个样子

HarmonyOS Next 中Media Kit使用说明及其封装音乐播放器及其背景音乐播放组件-鸿蒙开发者社区HarmonyOS Next 中Media Kit使用说明及其封装音乐播放器及其背景音乐播放组件-鸿蒙开发者社区

我们还需要添加一个函数用来转化时间:

  // 时间格式转换
  invertTime(time:number):string{
      let totalSeconds = Math.floor(time / 1000);
      let hours = Math.floor(totalSeconds / 3600);
      let minutes = Math.floor((totalSeconds % 3600) / 60);
      let seconds = totalSeconds % 60;

      // 格式化为两位数
      let formattedHours = String(hours).padStart(2, '0');
      let formattedMinutes = String(minutes).padStart(2, '0');
      let formattedSeconds = String(seconds).padStart(2, '0');

      return `${formattedHours}:${formattedMinutes}:${formattedSeconds}`;
    }

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.

4.添加页面

一切准备完整,我们只需要把页面加上,就会变成完整的样子

HarmonyOS Next 中Media Kit使用说明及其封装音乐播放器及其背景音乐播放组件-鸿蒙开发者社区HarmonyOS Next 中Media Kit使用说明及其封装音乐播放器及其背景音乐播放组件-鸿蒙开发者社区

完整代码:

import media from '@ohos.multimedia.media';
import fs from '@ohos.file.fs';
import common from '@ohos.app.ability.common';
import { BusinessError } from '@ohos.base';


@Entry
@Component
struct Index {

  private avPlayer: media.AVPlayer|null=null;

  // 存放歌曲信息的列表
  private songs:song_item[] = [];

  @State titlenumber:number=-1
  // 判断是否播放
  @State isBFplaying:Boolean=true

  // 进度条的值
  @State outSetValue: number = 0
  // 进度条长度
  @State SliderLength:number = 0



  aboutToAppear(): void {
    this.songs.push({Title:"花日",Author:"CMJ",SongFile:"CMJ.mp3"});
    this.songs.push({Title:"踊り子",Author:"Vaundy",SongFile:"Vaundy.mp3"});
  }

  // 播放设置
  async Play(item:song_item){
    if (this.avPlayer !== null) {
      this.avPlayer?.reset()
    }
    // 创建avPlayer实例对象
    this.avPlayer = await media.createAVPlayer();
    // 绑定回调函数
    this.setAVPlayerCallback(this.avPlayer);
    let context = getContext(this) as common.UIAbilityContext;
    let fileDescriptor = await context.resourceManager.getRawFd(item.SongFile);
    let avFileDescriptor: media.AVFileDescriptor =
      { fd: fileDescriptor.fd, offset: fileDescriptor.offset, length: fileDescriptor.length };
    // this.isSeek = true; // 支持seek操作
    // 为fdSrc赋值触发initialized状态机上报
    this.avPlayer.fdSrc = avFileDescriptor;
  }


  build() {
    Column(){
      Row(){
        Row(){
          Text('我的音乐')
            .fontColor(Color.White).fontSize(32)
        }.margin({left:20})
      }.backgroundColor("#71c9ce").height('8%').width('100%')
      Column(){
        List(){
          ForEach(this.songs,(item:song_item,index)=>{
            ListItem(){
              Row(){
                Button({type:ButtonType.Normal}){
                  Row(){
                    Text((index+1)+'  ')
                      .fontSize(32)
                    Column(){
                      Text(item.Title).fontSize(20).fontWeight(700)
                      Text(item.Author).fontSize(14)
                    }.alignItems(HorizontalAlign.Start)
                  }.justifyContent(FlexAlign.Start)
                  .width('90%')
                }
                .backgroundColor(Color.White)
                .width("100%").height(50)
                .margin({top:10})
                .onClick(()=>{

                  this.isBFplaying=true
                  this.titlenumber=index
                  this.Play(item);
                })
              }
            }
          })
        }.width('100%')
      }.height('84%')
      Row(){
        Column(){
          // 音乐信息
          Row(){
            if (this.titlenumber==-1){
              Text('点击歌曲开始播放')
                .fontSize(20).fontColor(Color.White)
            }else {
              Column(){
                Text(this.songs[this.titlenumber].Title)
                  .fontSize(20).fontColor(Color.White)
              }.width('70%').alignItems(HorizontalAlign.Start)
              Column(){
                Button({ type: ButtonType.Normal, stateEffect: true }){
                  Text(this.isBFplaying?"暂停":"继续")
                    .fontSize(20).fontColor(Color.White)
                }.borderRadius(8).height(26).width(70).backgroundColor("#40514e")
                .onClick(()=>{
                  // this.Play()
                  if (this.avPlayer !== null && this.isBFplaying==true) {
                    this.avPlayer.pause()
                    this.isBFplaying=!this.isBFplaying
                  }else{
                    this.avPlayer?.play()
                    this.isBFplaying=!this.isBFplaying
                  }
                })
              }.width('20%')
            }
          }.width('99%').margin({left:15})

          // 进度条

          Row() {
            Slider({
              value: this.outSetValue,
              min: 0,
              max: this.SliderLength,
              step: 1,
              style: SliderStyle.OutSet
            })
              .width("80%")
              .blockColor('#FFFFFF')
              .trackColor('#182431')
              .selectedColor('#cbf1f5')
              .showSteps(true)
              .showTips(false)
              .onChange((value: number, mode: SliderChangeMode) => {
                this.outSetValue = value
                // 调节长度
                this.avPlayer?.seek(this.outSetValue)
              })
            Text(this.invertTime(this.outSetValue)).fontSize(16)
          }

        }

      }.backgroundColor("#71c9ce").height('13%').width('100%')
    }.height('100%').width('100%')
  }


  setAVPlayerCallback(avPlayer: media.AVPlayer) {
    if (avPlayer !== null) {
      avPlayer.on('error', (err) => {
        console.error(`播放器发生错误,错误码:${err.code}, 错误信息:${err.message}`);
        avPlayer?.reset();
      });

      avPlayer.on('stateChange', async (state, reason) => {
        switch (state) {
          case 'initialized':
            console.info('资源初始化完成');
            avPlayer?.prepare();
            break;
          case 'prepared':
            console.info('资源准备完成');
            avPlayer?.play();
            break;
          case 'completed':
            console.info('播放完成');
            avPlayer?.stop();
            break;
        }
      });

      // 获取进度条长度
      avPlayer.on('timeUpdate', (time:number) => {
        this.outSetValue = time
        console.info('timeUpdate success,and new time is :' + time)

      })

      // 获取时间长度
      avPlayer.on('durationUpdate', (duration: number) => {

        this.SliderLength = duration;
        console.info('durationUpdate success,new duration is :' + duration)
      })
    }
  }

  // 时间格式转换
  invertTime(time:number):string{
      let totalSeconds = Math.floor(time / 1000);
      let hours = Math.floor(totalSeconds / 3600);
      let minutes = Math.floor((totalSeconds % 3600) / 60);
      let seconds = totalSeconds % 60;

      // 格式化为两位数
      let formattedHours = String(hours).padStart(2, '0');
      let formattedMinutes = String(minutes).padStart(2, '0');
      let formattedSeconds = String(seconds).padStart(2, '0');

      return `${formattedHours}:${formattedMinutes}:${formattedSeconds}`;
    }



}
interface song_item{
  // 歌曲名称
  Title:string
  // 作者
  Author:string
  // 歌曲文件
  SongFile:string

}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.
  • 113.
  • 114.
  • 115.
  • 116.
  • 117.
  • 118.
  • 119.
  • 120.
  • 121.
  • 122.
  • 123.
  • 124.
  • 125.
  • 126.
  • 127.
  • 128.
  • 129.
  • 130.
  • 131.
  • 132.
  • 133.
  • 134.
  • 135.
  • 136.
  • 137.
  • 138.
  • 139.
  • 140.
  • 141.
  • 142.
  • 143.
  • 144.
  • 145.
  • 146.
  • 147.
  • 148.
  • 149.
  • 150.
  • 151.
  • 152.
  • 153.
  • 154.
  • 155.
  • 156.
  • 157.
  • 158.
  • 159.
  • 160.
  • 161.
  • 162.
  • 163.
  • 164.
  • 165.
  • 166.
  • 167.
  • 168.
  • 169.
  • 170.
  • 171.
  • 172.
  • 173.
  • 174.
  • 175.
  • 176.
  • 177.
  • 178.
  • 179.
  • 180.
  • 181.
  • 182.
  • 183.
  • 184.
  • 185.
  • 186.
  • 187.
  • 188.
  • 189.
  • 190.
  • 191.
  • 192.
  • 193.
  • 194.
  • 195.
  • 196.
  • 197.
  • 198.
  • 199.
  • 200.
  • 201.
  • 202.
  • 203.
  • 204.
  • 205.
  • 206.
  • 207.
  • 208.
  • 209.
  • 210.
  • 211.
  • 212.
  • 213.
  • 214.
  • 215.
  • 216.
  • 217.

封装的背景音乐工具类

除此之外,我还封装了一个直接给我们的页面添加背景音乐的工具类

步骤都差不多,所以说实现过程不一一描述了

完整代码:

import media from '@ohos.multimedia.media';
import fs from '@ohos.file.fs';
import common from '@ohos.app.ability.common';
import { BusinessError } from '@ohos.base';

export class backgroundMusic{

  private avPlayer: media.AVPlayer|null=null;

  // 初始化函数
  public async init(music:string){

    if (this.avPlayer !== null) {
      this.avPlayer?.reset()
    }
    // this.musicFile = music;
    // 创建avPlayer实例对象
    this.avPlayer = await media.createAVPlayer();
    // 绑定回调函数
    this.setAVPlayerCallback(this.avPlayer);
    let context = getContext(this) as common.UIAbilityContext;
    let fileDescriptor = await context.resourceManager.getRawFd(music);
    let avFileDescriptor: media.AVFileDescriptor =
      { fd: fileDescriptor.fd, offset: fileDescriptor.offset, length: fileDescriptor.length };
    // this.isSeek = true; // 支持seek操作
    // 为fdSrc赋值触发initialized状态机上报
    this.avPlayer.fdSrc = avFileDescriptor;

  }

  // 播放函数
  public play(){
    this.avPlayer?.play();

  }

  // 暂停函数
  public paused(){
    this.avPlayer?.pause();
  }

  // 其他函数
  setAVPlayerCallback(avPlayer: media.AVPlayer) {
    if (avPlayer !== null) {
      avPlayer.on('error', (err) => {
        console.error(`播放器发生错误,错误码:${err.code}, 错误信息:${err.message}`);
        avPlayer?.reset();
      });

      avPlayer.on('stateChange', async (state, reason) => {
        switch (state) {
          case 'initialized':
            console.info('资源初始化完成');
            avPlayer?.prepare();
            break;
          case 'prepared':
            console.info('资源准备完成');
            avPlayer?.play();
            break;
          case 'completed':
            console.info('播放完成');
            avPlayer?.stop();
            break;
        }
      });
    }
  }

}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.

使用方法:

在我们需要添加背景音乐的页面进行初始化操作

  backgroundmusic:backgroundMusic = new backgroundMusic();
  aboutToAppear(): void {
    this.backgroundmusic.init("CMJ.mp3")
  }
  • 1.
  • 2.
  • 3.
  • 4.

如果需要暂停或播放可以使用

this.backgroundmusic.paused()
  • 1.

this.backgroundmusic.play()
  • 1.

页面完整代码和展示:

import media from '@ohos.multimedia.media';
import fs from '@ohos.file.fs';
import common from '@ohos.app.ability.common';
import { BusinessError } from '@ohos.base';
import { backgroundMusic } from './backgroundMusic'


@Entry
@Component
struct Index {

  @State isPlay:boolean = true
  backgroundmusic:backgroundMusic = new backgroundMusic();
  
  aboutToAppear(): void {
    this.backgroundmusic.init("CMJ.mp3")
  }

  build() {
    Column(){
      Row(){
          Text('背景音乐')
            .fontColor(Color.White).fontSize(32)
            .margin({left:20,right:20})

          Image(this.isPlay?$r("app.media.paused"):$r("app.media.play"))
            .height("40vp")
            .margin({left:20,right:20})
            .onClick(()=>{
              if (this.isPlay == true) {
                this.backgroundmusic.paused()
              }else {
                this.backgroundmusic.play()
              }
              this.isPlay = !this.isPlay
              
            })

      }.backgroundColor("#71c9ce").height('8%').width('100%')
      .justifyContent(FlexAlign.SpaceBetween)
      Column(){
        Text(this.isPlay?"正在播放":"正在暂停")
          .fontSize(32)
      }
      .justifyContent(FlexAlign.Center)
      .height('92%').width('100%')

    }.height('100%').width('100%')
  }



}

  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.

展示效果

HarmonyOS Next 中Media Kit使用说明及其封装音乐播放器及其背景音乐播放组件-鸿蒙开发者社区HarmonyOS Next 中Media Kit使用说明及其封装音乐播放器及其背景音乐播放组件-鸿蒙开发者社区

结尾

小工具及其Media Kit的解释,需要的直接复制过来用

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
2
收藏
回复
举报
2


回复
    相关推荐