AVplayer实现音乐播放器

前言

使用AVPlayer 可以实现端到端播放原始媒体资源,如需播放PCM音频数据,可以使用 AudioRenderer
AVplayer有几个重要的属性和方法
首先需要需要创建AVplayer实例:media.createAVPlayer()
然后需要确定播放的资源路径,赋值给实例player.url
初始化实例的同时,需要对之后发生的事件进行监听,以便后续获取相关数据(例如资源的时长,当前播放了多少时间等等)
播放资源使用play()方法,但是这个在监听中使用
当然这里涉及到消息发送和订阅,emitter.emit和emitter.on,eventId是消息的唯一标识,然后用data属性来传递和接收数据
此外,资源来源于黑马程序员的接口

效果

在这里插入图片描述
在这里插入图片描述

代码

播放资源
constant/MusicConstant.ets

import { SongType } from '../model/music'

// 歌曲列表数据
export const songs: SongType[] = [
  {
    img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/0.jpg',
    name: '直到世界的尽头',
    author: 'WANDS',
    url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/0.m4a',
    id: '0000'
  },
  {
    img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/1.jpg',
    name: '画',
    author: '赵磊',
    url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/1.mp3',
    id: '0001'
  },
  {
    img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/2.jpg',
    name: 'Sweet Dreams',
    author: 'TPaul Sax / Eurythmics',
    url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/2.mp3',
    id: '0002'
  },
  {
    img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/3.jpg',
    name: '奢香夫人',
    author: '凤凰传奇',
    url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/3.m4a',
    id: '0003'
  },
  {
    img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/4.jpg',
    name: '空心',
    author: '光泽',
    url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/4.mp3',
    id: '0004'
  },
  {
    img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/5.jpg',
    name: '反转地球',
    author: '潘玮柏',
    url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/5.mp3',
    id: '0005'
  },
  {
    img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/6.jpg',
    name: 'No.9',
    author: 'T-ara',
    url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/6.m4a',
    id: '0006'
  },
  {
    img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/7.jpg',
    name: '孤独',
    author: 'G.E.M.邓紫棋',
    url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/7.m4a',
    id: '0007'
  },

  {
    img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/8.jpg',
    name: 'Lose Control',
    author: 'Hedley',
    url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/8.m4a',
    id: '0008'
  },
  {
    img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/9.jpg',
    name: '倩女幽魂',
    author: '张国荣',
    url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/9.m4a',
    id: '0009'
  },
  {
    img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/10.jpg',
    name: '北京北京',
    author: '汪峰',
    url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/10.m4a',
    id: '0010'
  },
  {
    img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/11.jpg',
    name: '苦笑',
    author: '汪苏泷',
    url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/11.mp3',
    id: '0011'
  },
  {
    img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/12.jpg',
    name: '一生所爱',
    author: '卢冠廷 / 莫文蔚',
    url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/12.m4a',
    id: '0012'
  },
  {
    img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/13.jpg',
    name: '月半小夜曲',
    author: '李克勤',
    url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/13.mp3',
    id: '0013'
  },
  {
    img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/14.jpg',
    name: 'Rolling in the Deep',
    author: 'Adele',
    url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/14.m4a',
    id: '0014'
  },
  {
    img: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/15.jpg',
    name: '海阔天空',
    author: 'Beyond',
    url: 'http://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/HeimaCloudMusic/15.m4a',
    id: '0015'
  }
]

音乐类型的声明
model/Music.ets

export interface SongType {
  img: string
  name: string
  author: string
  url: string
  id: string
}

export class SongTypeModel implements SongType {
  img: string = ''
  name: string = ''
  author: string = ''
  url: string = ''
  id: string = ''

  constructor(model: SongType) {
    this.img = model.img
    this.name = model.name
    this.author = model.author
    this.url = model.url
    this.id = model.id
  }
}

export interface PlayState{
  duration:number
  time:number
  isPlay:boolean
  playMode:'auto'|'repeat'|'random'
  playList:SongType[],
  playIndex:number,
  img:string,
  name:string,
  author:string,
}

AVPlayer的工具类
utils/AVPlayerClass.ets

import { media } from '@kit.MediaKit'
import { SongType } from '../model/music'
import { emitter } from '@kit.BasicServicesKit'

export class AVPlayerClass {
  //----------属性
  // 1.播放器
  static player: media.AVPlayer = {} as media.AVPlayer
  //2.歌曲时长
  static duration: number = 0
  //3.墙放时长
  static time: number = 0
  //4.是否播放
  static isPlay: boolean = false
  //5.播放类型
  static playMode: 'auto' | 'repeat' | 'random' = 'auto'
  //6.播放列表
  static playList: SongType[] = []
  //7.记录当前播放索引
  static playIndex: number = 0

  //------- 方法
  //创建播放器
  static async init() {
    //创建
    AVPlayerClass.player = await media.createAVPlayer()
    //监听状态
    AVPlayerClass.player.on('stateChange', (state) => {
      switch (state) {
      //初始化
        case 'initialized':
          AVPlayerClass.player.prepare()
          break
      //准备
        case 'prepared':
          AVPlayerClass.player.play()
          break
      }
    })
    //监听歌曲的时长
    AVPlayerClass.player.on('durationUpdate', (duration) => {
      AVPlayerClass.duration = duration
    })
    //监听歌曲的播放时长
    AVPlayerClass.player.on('timeUpdate', (time) => {
      AVPlayerClass.time = time
      AVPlayerClass.updateState()
    })
  }

  //单曲播放
  static async singlePlay(song: SongType) {
    // //将歌曲添加到列表,更新播放索引
    //   AVPlayerClass.playList.unshift(song)
    //   AVPlayerClass.playIndex = 0
    //   //让播放器处于闲置状态
    //   await AVPlayerClass.player.reset()
    // //设置播放源
    //   AVPlayerClass.player.url = song.url
    const index = AVPlayerClass.playList.findIndex(item => item.id === song.id)

    if (index >= 0) {
      if (AVPlayerClass.player.url === song.url) {
        //继续
      } else {
        await AVPlayerClass.player.reset()
        AVPlayerClass.player.url = song.url
      }
    }else {
      AVPlayerClass.playList.unshift(song)
      AVPlayerClass.playIndex = 0

      await AVPlayerClass.player.reset()
      AVPlayerClass.player.url = song.url
    }
  }


  // 发布信息
  static updateState(){
    emitter.emit({
      eventId:0
    },{
      data:{
        duration:AVPlayerClass.duration,
        time:AVPlayerClass.time,
        isPlay:AVPlayerClass.isPlay,
        playMode:AVPlayerClass.playMode,
        playList:AVPlayerClass.playList,
        playIndex:AVPlayerClass.playIndex,
        img:AVPlayerClass.playList[AVPlayerClass.playIndex].img,
        name:AVPlayerClass.playList[AVPlayerClass.playIndex].name,
        author:AVPlayerClass.playList[AVPlayerClass.playIndex].author,
      }
    })
  }

  //暫停播放
  static pause(){
    AVPlayerClass.player.pause()
  }
}

歌曲列表页
pages/Index.ets

import { songs } from '../constant/MusicConstant'
import router from '@ohos.router'
import { SongType } from '../model/music'
import { AVPlayerClass } from '../utils/AVPlayerClass'

@Entry
@Component
struct Index {
  build() {
    Column() {
      Row() {
        Text('猜你喜欢')
          .fontColor('#fff')
      }
      .width('100%')
      .padding(24)
      Row() {
        List({ space: 10 }) {
          ForEach(songs, (song: SongType) => {
            ListItem() {
              Row() {
                Image(song.img)
                  .width(60)
                  .borderRadius(8)
                Column({ space: 10 }) {
                  Text(song.name)
                    .fontColor('#fff')
                  Row({ space: 5 }) {
                    Text('VIP')
                      .fontSize(14)
                      .fontColor(Color.Orange)
                      .padding({ left: 8, right: 8, top: 2, bottom: 2 })
                      .borderRadius(16)
                      .border({
                        width: 1,
                        color: Color.Orange
                      })
                    Text(song.author)
                      .fontColor('#fff')
                  }
                }
                .padding({
                  left: 16
                })
                .layoutWeight(1)
                .alignItems(HorizontalAlign.Start)

                Image($r('app.media.ic_more'))
                  .width(20)
              }
            }
            .onClick(() => {
              router.pushUrl({
                url: 'pages/PlayPage',
              })
              AVPlayerClass.singlePlay(song)
            })
          })
          ListItem(){
            Text('没有更多啦~')
              .padding(8)
              .width('100%')
              .fontColor('#ccc')
              .textAlign(TextAlign.Center)
          }
        }
        .width('100%')
        .height('100%')
        .padding({
          left: 24,
          right: 24
        })
      }
      .layoutWeight(1)
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#111113')
    .expandSafeArea([SafeAreaType.SYSTEM],[SafeAreaEdge.TOP,SafeAreaEdge.BOTTOM])
  }
}

播放页面
pages/PlayPage.ets

import { emitter } from '@kit.BasicServicesKit'
import { PlayState } from '../model/Music'
import { AVPlayerClass } from '../utils/AVPlayerClass'

@Entry
@Component
struct PlayPage {
  @State playState: PlayState = {} as PlayState
  @State isPlay:boolean = true
  number2time(number: number) {
    const second = Math.floor(number / 1000)
    if (second >= 60) {
      const m = Math.floor(second / 60)
      return `${m.toString().padStart(2,'0')}:${second%60}`
    } else {
      return `00:${second.toString().padStart(2, '0')}`
    }
  }

  aboutToAppear(): void {
    //订阅数据
    emitter.on(
      { eventId: 0 },
      (data) => {
        this.playState = data.data as PlayState
      }
    )
  }

  build() {
    // 播放
    Column() {
      // 图片
      Stack({ alignContent: Alignment.Top }) {
        Row() {
          Row() {
            Image(this.playState.img)
              .width('70%')
              .borderRadius(400)
          }
          .backgroundImage($r('app.media.ic_cd'))
          .backgroundImageSize(ImageSize.Cover)
          .justifyContent(FlexAlign.Center)
          .width('100%')
          .borderRadius(400)
          .clip(true)
          .aspectRatio(1)
        }
        .margin({
          top: 50
        })
        .width('90%')
        .aspectRatio(1)
        .justifyContent(FlexAlign.Center)
        .padding(24)

        // 唱针
        Image($r('app.media.ic_stylus'))
          .width(200)
          .aspectRatio(1)
          .rotate({
            angle: -55,
            centerX: 100,
            centerY: 30
          })
          .animation({
            duration: 500
          })
      }

      // 歌曲信息
      Stack() {
        // 第一个
        Column({ space: 8 }) {
          Text(this.playState.name)
            .fontSize(28)
            .fontWeight(FontWeight.Bold)
            .fontColor('#4bb0c4')
          Text(this.playState.author)
            .fontSize(18)
            .fontColor('#4bb0c4')
        }
        .layoutWeight(1)
        .justifyContent(FlexAlign.Center)
        .zIndex(1)

        // 第二个
        Column({ space: 8 }) {
          Text(this.playState.name)
            .fontSize(28)
            .fontWeight(FontWeight.Bold)
            .fontColor('#ec5c87')
          Text(this.playState.author)
            .fontSize(18)
            .fontColor('#ec5c87')
        }
        .layoutWeight(1)
        .justifyContent(FlexAlign.Center)
        .zIndex(2)

        // 第三个
        Column({ space: 8 }) {
          Text(this.playState.name)
            .fontSize(28)
            .fontWeight(FontWeight.Bold)
            .fontColor(Color.White)
          Text(this.playState.author)
            .fontSize(18)
            .fontColor(Color.White)
        }
        .layoutWeight(1)
        .justifyContent(FlexAlign.Center)
        .zIndex(3)
      }
      .layoutWeight(1)

      // 操作
      Row() {
        Badge({ value: '99+', style: { badgeSize: 12, badgeColor: '#45CCCCCC' } }) {
          Image($r("app.media.ic_like"))
            .fillColor(Color.White)
            .width(24)
        }

        Badge({ value: '10W', style: { badgeSize: 12, badgeColor: '#45cccccc' } }) {
          Image($r("app.media.ic_comment_o"))
            .fillColor(Color.White)
            .width(18)
        }

        Badge({ value: 'hot', style: { badgeSize: 12, badgeColor: '#a8ff3131' } }) {
          Image($r("app.media.ic_bells_o"))
            .fillColor(Color.White)
            .width(24)
        }

        Badge({ value: 'vip', style: { badgeSize: 12, badgeColor: '#b7efd371' } }) {
          Image($r("app.media.ic_download_o"))
            .fillColor(Color.White)
            .width(24)
        }
      }
      .width('100%')
      .justifyContent(FlexAlign.SpaceAround)

      // 播放
      Column() {
        // 进度条
        Row({ space: 4 }) {
          Text(this.number2time(this.playState.time))
            .fontSize(12)
            .fontColor(Color.White)
          Slider({
            value: this.playState.time,
            min: 0,
            max: this.playState.duration
          })
            .layoutWeight(1)
            .blockColor(Color.White)
            .selectedColor(Color.White)
            .trackColor('#ccc5c5c5')
            .trackThickness(2)
          Text(this.number2time(this.playState.duration))
            .fontSize(12)
            .fontColor(Color.White)
        }
        .width('100%')
        .padding(24)

        // 切换
        Row() {
          Image($r('app.media.ic_auto'))
            .fillColor(Color.White)
            .width(30)

          Image($r("app.media.ic_prev"))
            .fillColor(Color.White)
            .width(30)

          // 播放按钮
          Image($r('app.media.ic_pause'))
            .fillColor(Color.White)
            .width(50)
            .onClick(()=>{
              if (this.isPlay) {

              }else {

              }
            })
          // 下一首
          Image($r('app.media.ic_next'))
            .fillColor(Color.White)
            .width(30)
          // 播放列表
          Image($r('app.media.ic_song_list'))
            .fillColor(Color.White)
            .width(30)
        }
        .width('100%')
        .padding({
          bottom: 24
        })
        .justifyContent(FlexAlign.SpaceAround)
      }
      .width('100%')
    }
    .width('100%')
    .height('100%')
    .backgroundImage(this.playState.img)
    .backgroundImageSize(ImageSize.Cover)
    .backgroundBlurStyle(BlurStyle.BACKGROUND_ULTRA_THICK)
    .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])
  }
}

这些代码我是跟着黑马写的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值