基于API9的音乐播放器,可播放,暂停,上,下一首歌曲切换等功能
文章目录
1.效果展示
2.首页
3.播放页
注:需要使用模拟器或真机调试运行
一、效果展示
1.首页样式:
2.播放样式:
二、首页功能代码
1: UI代码
首页分为2部分,头部和底部。纵向布局,比例1:3。
1.1:头部
//头部Row(){Column(){Image('https://tse2-mm.cn.bing.net/th/id/OIP-C.fNF8owgmIwYhU9KINmt2dAAAAA?w=217&h=217&c=7&r=0&o=5&dpr=1.5&pid=1.7').width(80).height(80).borderRadius(50)Column({space:5}){//获取时间Text(new Date().toDateString()).fontSize(20).fontColor(Color.White).fontWeight(800)Text('1 YEAR ACO TODAY').fontSize(16).opacity(0.6)//透明度}}.width('100%').height('100%').justifyContent(FlexAlign.SpaceAround)}.width('100%').layoutWeight(1).backgroundColor('#4FE3C1').justifyContent(FlexAlign.Center)
1.2:底部
底部使用foreach循环渲染每个歌曲的信息,另外当所需渲染的歌曲过多的时候,我们需要使用Scroll组件,让他歌曲可以滚动以便展示完整歌曲。
//歌单列表Row(){//滚动组件Scroll(){Column({space:20}){//this.songList==》歌曲所有信息,songInfo==》歌曲信息类ForEach(this.songList,(item:songInfo,index:number)=>{//歌曲信息组件Row(){Row({space:10}){Text(item.id.toString()).fontSize(24)//歌曲idImage(item.pic).width(60).height(60)//歌曲图片Column(){//歌曲名称(限定歌曲长度为1行,超出则显示...)Text(item.name).width('60%').fontSize(18).fontWeight(700).maxLines(1).textOverflow({overflow:TextOverflow.Ellipsis })//歌曲作者Text(item.author).fontSize(15).opacity(0.6)}.height(80).alignItems(HorizontalAlign.Start).justifyContent(FlexAlign.SpaceEvenly)}.width('80%')Row(){Text(item.play+'Plays').fontSize(12).fontColor(Color.Gray)//歌曲播放数}.width('20%').height(80).justifyContent(FlexAlign.End).alignItems(VerticalAlign.Bottom)}.width('100%').height(100).borderWidth(1).borderRadius(10).shadow({radius:10}).padding(10)})}.padding({top:20,bottom:20,left:5,right:5})}.scrollBar(BarState.Off).width('100%').height('100%')}.width('100%').layoutWeight(3)
2:其他代码
2.1:首页变量数据
import { song } from '../mock/song'import { songInfo } from '../model/songInfo'//播放的歌曲信息@StorageLink('song') songList:songInfo[]=[]//播放的歌曲索引@StorageLink('i') currentIndex:number = -1aboutToAppear(){this.songList = song.songList//赋值歌曲信息AppStorage.SetOrCreate('i',-1)//初始化}
2.2:song文件代码
import { songInfo } from '../model/songInfo'
export class song{static songList:songInfo[]=[new songInfo(1,$rawfile('OnlyLovers.png'),'Only Lovers','Trademark',24,541,999,'onlyLovers.mp3'),new songInfo(2,$rawfile('NeverReallyEasy.png'),'Never Really Easy','SSerafim',33,779,999,'NeverReallyEasy.mp3'),new songInfo(3,$rawfile('BumpingUpandDown.png'),'Bumping Upand Down','MCND',40,956,999,'BumpingUpandDown.mp3'),new songInfo(4,$rawfile('BattleField.png'),'Battle Field','JordanFisher',57,978,999,'BattleField.mp3'),new songInfo(5,$rawfile('CanYouFeelIt.png'),'Can You Feel It','JeanRoch',61,988,999,'CanYouFeelIt.mp3'),new songInfo(6,$rawfile('GodIsAGirl.png'),'God Is A Girl','Groove',75,990,999,'GodIsAGirl.mp3'),new songInfo(7,$rawfile('TuViviNell‘aria.png'),'Tu Vivi Nell','Miani',95,999,999,'TuViviNell‘aria.mp3'),]
}
歌曲文件在以下目录中:
2.3:songInfo文件代码
export class songInfo{id:number//歌曲idpic:Resource//歌曲图片name:string//歌曲姓名author:string//歌曲作者play:number//播放次数like:number//喜欢总数comment:number//评论数src:string//播放地址constructor(id:number,pic:Resource,name:string,author:string,play:number,like:number,comment:number,src:string) {this.id = idthis.pic = picthis.name = namethis.author = authorthis.play = playthis.like = likethis.comment = commentthis.src = src}
}
3:点击事件
当用户点击其中任意一首歌曲时,需要跳转到其播放页。由于数据是死的,我们通过传songList中的索引数据过去,就能实现播放对应的歌曲。
代码:
//歌曲信息组件Row(){...... }.width('100%').height(100).borderWidth(1).borderRadius(10).shadow({radius:10}).padding(10).onClick(()=>{//歌曲信息组件点击事件router.pushUrl({url:'pages/Player'})this.currentIndex = index})
4:完整代码
import router from '@ohos.router'
import { song } from '../mock/song'
import { songInfo } from '../model/songInfo'
@Entry
@Component
struct Index {//播放的歌曲信息@StorageLink('song') songList:songInfo[]=[]//播放的歌曲索引@StorageLink('i') currentIndex:number = -1aboutToAppear(){this.songList = song.songListAppStorage.SetOrCreate('i',-1)}build() {Column(){//头部Row(){Column(){Image('https://tse2-mm.cn.bing.net/th/id/OIP-C.fNF8owgmIwYhU9KINmt2dAAAAA?w=217&h=217&c=7&r=0&o=5&dpr=1.5&pid=1.7').width(80).height(80).borderRadius(50)Column({space:5}){//获取时间Text(new Date().toDateString()).fontSize(20).fontColor(Color.White).fontWeight(800)Text('1 YEAR ACO TODAY').fontSize(16).opacity(0.6)//透明度}}.width('100%').height('100%').justifyContent(FlexAlign.SpaceAround)}.width('100%').layoutWeight(1).backgroundColor('#4FE3C1').justifyContent(FlexAlign.Center)//歌单列表Row(){//滚动组件Scroll(){Column({space:20}){//this.songList==》歌曲所有信息,songInfo==》歌曲信息类ForEach(this.songList,(item:songInfo,index:number)=>{//歌曲信息组件Row(){Row({space:10}){Text(item.id.toString()).fontSize(24)//歌曲idImage(item.pic).width(60).height(60)//歌曲图片Column(){//歌曲名称(限定歌曲长度为1行,超出则显示...)Text(item.name).width('60%').fontSize(18).fontWeight(700).maxLines(1).textOverflow({overflow:TextOverflow.Ellipsis })//歌曲作者Text(item.author).fontSize(15).opacity(0.6)}.height(80).alignItems(HorizontalAlign.Start).justifyContent(FlexAlign.SpaceEvenly)}.width('80%')Row(){Text(item.play+'Plays').fontSize(12).fontColor(Color.Gray)//歌曲播放数}.width('20%').height(80).justifyContent(FlexAlign.End).alignItems(VerticalAlign.Bottom)}.width('100%').height(100).borderWidth(1).borderRadius(10).shadow({radius:10}).padding(10).onClick(()=>{//歌曲信息组件点击事件router.pushUrl({url:'pages/Player'})this.currentIndex = index})})}.padding({top:20,bottom:20,left:5,right:5})}.scrollBar(BarState.Off).width('100%').height('100%')}.width('100%').layoutWeight(3)}.width('100%').height('100%')}
}
三 、播放功能代码
1:UI代码
1.1:头部
//头部Row(){Column({space:5}){Text(new Date().toDateString()).fontSize(22).fontWeight(800)Text('1 YEAR ACO TODAY').fontSize(15).fontColor(Color.Gray).opacity(0.6)}}.width('100%').layoutWeight(1).justifyContent(FlexAlign.Center)
1.2:空白占位区
//空白占位Row(){}.width('100%').layoutWeight(1)
1.3:歌曲图片及播放进度
//歌曲图片和播放进度Row(){Stack(){Image(this.songList[this.currentIndex].pic).width(170).height(170).borderRadius(100)Progress({value:this.value,total:this.total,type:ProgressType.Ring}).width(190).height(190).color("#ff49d7b8").style({strokeWidth:8})}}.width('100%').layoutWeight(4).justifyContent(FlexAlign.Center)
1.4:歌曲信息
//歌曲信息Row(){Column({space:10}){Text(this.songList[this.currentIndex].name).fontSize(25).fontWeight(900)Text(this.songList[this.currentIndex].author).fontSize(18).opacity(0.6)//喜欢,评论,分享Row(){Badge({count:this.songList[this.currentIndex].like,position:BadgePosition.RightTop,style:{badgeSize:20,badgeColor:Color.Red}}){Image($r('app.media.tool_like')).width(40).height(40)}Badge({count:this.songList[this.currentIndex].comment,position:BadgePosition.RightTop,style:{badgeSize:20,badgeColor:Color.Gray}}){Image($r('app.media.tool_comment')).width(40).height(40)}Image($r('app.media.nav_share')).width(40).height(40)}.width('100%').justifyContent(FlexAlign.SpaceAround).margin({top:10})}.width('100%').height('100%').justifyContent(FlexAlign.Center)}.width('100%').layoutWeight(2)
1.5:歌曲菜单控制
//歌曲菜单Row(){Row(){Image($r('app.media.control_last')).width(50).height(50).onClick(async()=>{})Image(this.isPlaying ? $r('app.media.control_pause') : $r('app.media.control_play')).width(50).height(50).onClick(()=>{this.isPlaying ? this.avPlayer.pause() : this.avPlayer.play()})Image($r('app.media.control_next')).width(50).height(50).onClick(async()=>{})}.width('100%').justifyContent(FlexAlign.SpaceEvenly).margin({top:10})}.width('100%').layoutWeight(1)
2:其他代码(重要)
2.1:播放页变量数据
//歌曲信息@StorageLink('song') songList:songInfo[]=[]//播放的歌曲索引@StorageLink('i') currentIndex:number = -1//现在播放的时间@State value:number = 0//总共播放的时间@State total:number = 0//是否开启定时器标志@State flag:boolean = false//定时器id@State time:number=0//是否正在播放@State isPlaying:boolean = false//音乐播放对象avPlayer:media.AVPlayer
2.2:音乐播放状态及事件
//音乐播放状态setAVPlayerCallback(avPlayer:media.AVPlayer){// error回调监听函数,当avPlayer在操作过程中出现错误时调用 reset接口触发重置流程avPlayer.on('error', (err) => {console.error(`Invoke avPlayer failed, code is ${err.code}, message is ${err.message}`);avPlayer.reset(); // 调用reset重置资源,触发idle状态})avPlayer.on('stateChange',async(state:string)=>{switch (state){case 'idle': // 成功调用reset接口后触发该状态机上报avPlayer.release(); // 调用release接口销毁实例对象break;case 'initialized': // avplayer 设置播放源后触发该状态上报avPlayer.prepare(); // 调用prepare接口销毁实例对象break;case 'prepared': // prepare调用成功后上报该状态机this.value = avPlayer.currentTime // 播放的时间进度this.total = avPlayer.duration // 歌曲总共时间avPlayer.play(); // 调用play接口开始播放break;case 'playing':this.isPlaying = true // 开始播放this.flag = true // 状态标记this.setTimer() // 启动定时器break;case 'paused':this.isPlaying = false //未开始播放this.deleteTimer() //暂停定时器avPlayer.pause() // 调用pause接口暂停播放break;case 'stopped':avPlayer.reset(); // 调用reset接口初始化avplayer状态break;case 'released':break;}})}
2.3:获取音乐歌曲播放路径
//获取音乐播放路径async getAVPlayerUrl(){//创建播放实例对象let avPlayer = await media.createAVPlayer()this.setAVPlayerCallback(avPlayer)//获取上下文对象let context = getContext(this) as common.UIAbilityContext//获取播放文件let file = await context.resourceManager.getRawFd(this.songList[this.currentIndex].src)let avFile:media.AVFileDescriptor = {fd:file.fd,offset:file.offset,length:file.length}avPlayer.fdSrc = avFilereturn avPlayer}
这里的代码不懂的可以参考官网文档,即在我们获取到播放状态时候,添加了处理事件。
官网链接:使用AVPlayer开发音频播放功能(ArkTS) (openharmony.cn)
2.4:初始化
async aboutToAppear(){this.avPlayer = await this.getAVPlayerUrl()}
2.5:定时器功能
//定时器(持续增加播放的时间)setTimer(){if(this.flag){this.time = setInterval(()=>{//如果当值大于总的播放时间的时候,那么就取消定时if(this.value++ > this.total){this.deleteTimer()}else {this.value++}},1)}}//取消定时器deleteTimer(){clearInterval(this.time)this.flag = false}//重置时间resetTime(){this.deleteTimer()this.value = 0this.total = 0}
3:点击事件
//上一首Image($r('app.media.control_last')).width(50).height(50).onClick(async()=>{this.avPlayer.release()if(this.currentIndex-1 < 0){this.currentIndex = this.songList.length -1}else {this.currentIndex--}this.resetTime()this.avPlayer = await this.getAVPlayerUrl()this.avPlayer.play()})//暂停/播放Image(this.isPlaying ? $r('app.media.control_pause') : $r('app.media.control_play')).width(50).height(50).onClick(()=>{this.isPlaying ? this.avPlayer.pause() : this.avPlayer.play()})//下一首Image($r('app.media.control_next')).width(50).height(50).onClick(async()=>{this.avPlayer.release()if(this.currentIndex+1 > song.songList.length){this.currentIndex = 0}else {this.currentIndex++}this.resetTime()this.avPlayer = await this.getAVPlayerUrl()this.avPlayer.play()})
如果此时播放的歌曲是第一首的情况下,那么当用户点击播放上一首的时候,则播放歌曲列表的最后一首歌曲,播放下一首也是同理。
4:完整代码
import media from '@ohos.multimedia.media'
import { songInfo } from '../model/songInfo'
import common from '@ohos.app.ability.common'
import { song } from '../mock/song'
@Entry
@Component
struct Player {//歌曲信息@StorageLink('song') songList:songInfo[]=[]//播放的歌曲索引@StorageLink('i') currentIndex:number = -1//现在播放的时间@State value:number = 0//总共播放的时间@State total:number = 0//是否开启定时器标志@State flag:boolean = false//定时器id@State time:number=0//是否正在播放@State isPlaying:boolean = false//音乐播放对象avPlayer:media.AVPlayerasync aboutToAppear(){this.avPlayer = await this.getAVPlayerUrl()}//定时器(持续增加播放的时间)setTimer(){if(this.flag){this.time = setInterval(()=>{if(this.value++ > this.total){this.deleteTimer()}else {this.value++}},1)}}//取消定时器deleteTimer(){clearInterval(this.time)this.flag = false}//重置时间resetTime(){this.deleteTimer()this.value = 0this.total = 0}build() {Column(){//头部Row(){Column({space:5}){Text(new Date().toDateString()).fontSize(22).fontWeight(800)Text('1 YEAR ACO TODAY').fontSize(15).fontColor(Color.Gray).opacity(0.6)}}.width('100%').layoutWeight(1).justifyContent(FlexAlign.Center)//空白占位Row(){}.width('100%').layoutWeight(1)//歌曲图片和播放进度Row(){Stack(){Image(this.songList[this.currentIndex].pic).width(170).height(170).borderRadius(100)Progress({value:this.value,total:this.total,type:ProgressType.Ring}).width(190).height(190).color("#ff49d7b8").style({strokeWidth:8})}}.width('100%').layoutWeight(4).justifyContent(FlexAlign.Center)//歌曲信息Row(){Column({space:10}){Text(this.songList[this.currentIndex].name).fontSize(25).fontWeight(900)Text(this.songList[this.currentIndex].author).fontSize(18).opacity(0.6)//喜欢,评论,分享Row(){Badge({count:this.songList[this.currentIndex].like,position:BadgePosition.RightTop,style:{badgeSize:20,badgeColor:Color.Red}}){Image($r('app.media.tool_like')).width(40).height(40)}Badge({count:this.songList[this.currentIndex].comment,position:BadgePosition.RightTop,style:{badgeSize:20,badgeColor:Color.Gray}}){Image($r('app.media.tool_comment')).width(40).height(40)}Image($r('app.media.nav_share')).width(40).height(40)}.width('100%').justifyContent(FlexAlign.SpaceAround).margin({top:10})}.width('100%').height('100%').justifyContent(FlexAlign.Center)}.width('100%').layoutWeight(2)//歌曲菜单Row(){Row(){//上一首Image($r('app.media.control_last')).width(50).height(50).onClick(async()=>{this.avPlayer.release()if(this.currentIndex-1 < 0){this.currentIndex = this.songList.length -1}else {this.currentIndex--}this.resetTime()this.avPlayer = await this.getAVPlayerUrl()this.avPlayer.play()})//暂停/播放Image(this.isPlaying ? $r('app.media.control_pause') : $r('app.media.control_play')).width(50).height(50).onClick(()=>{this.isPlaying ? this.avPlayer.pause() : this.avPlayer.play()})//下一首Image($r('app.media.control_next')).width(50).height(50).onClick(async()=>{this.avPlayer.release()if(this.currentIndex+1 > song.songList.length){this.currentIndex = 0}else {this.currentIndex++}this.resetTime()this.avPlayer = await this.getAVPlayerUrl()this.avPlayer.play()})}.width('100%').justifyContent(FlexAlign.SpaceEvenly).margin({top:10})}.width('100%').layoutWeight(1)//占位Row(){}.width('100%').layoutWeight(1).backgroundColor("#ff49d7b8")}.width('100%').height('100%')}//音乐播放状态setAVPlayerCallback(avPlayer:media.AVPlayer){// error回调监听函数,当avPlayer在操作过程中出现错误时调用 reset接口触发重置流程avPlayer.on('error', (err) => {console.error(`Invoke avPlayer failed, code is ${err.code}, message is ${err.message}`);avPlayer.reset(); // 调用reset重置资源,触发idle状态})avPlayer.on('stateChange',async(state:string)=>{switch (state){case 'idle': // 成功调用reset接口后触发该状态机上报avPlayer.release(); // 调用release接口销毁实例对象break;case 'initialized': // avplayer 设置播放源后触发该状态上报avPlayer.prepare(); // 调用prepare接口销毁实例对象break;case 'prepared': // prepare调用成功后上报该状态机this.value = avPlayer.currentTime // 播放的时间进度this.total = avPlayer.duration // 歌曲总共时间avPlayer.play(); // 调用play接口开始播放break;case 'playing':this.isPlaying = true // 开始播放this.flag = true // 状态标记this.setTimer() // 启动定时器break;case 'paused':this.isPlaying = false //未开始播放this.deleteTimer() //暂停定时器avPlayer.pause() // 调用pause接口暂停播放break;case 'stopped':avPlayer.reset(); // 调用reset接口初始化avplayer状态break;case 'released':break;}})}//获取音乐播放路径async getAVPlayerUrl(){//创建播放实例对象let avPlayer = await media.createAVPlayer()this.setAVPlayerCallback(avPlayer)//获取上下文对象let context = getContext(this) as common.UIAbilityContext//获取播放文件let file = await context.resourceManager.getRawFd(this.songList[this.currentIndex].src)let avFile:media.AVFileDescriptor = {fd:file.fd,offset:file.offset,length:file.length}avPlayer.fdSrc = avFilereturn avPlayer}
}
总结
利用AVPlayer开发音频播放,重点是掌握如何获取播放路径(本地地址,网络地址)等,再根据歌曲的状态编写对应函数即可。