前言
使用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])
}
}
这些代码我是跟着黑马写的