基于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)//歌曲id
- Image(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 = -1
- aboutToAppear(){
- 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//歌曲id
- pic: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 = id
- this.pic = pic
- this.name = name
- this.author = author
- this.play = play
- this.like = like
- this.comment = comment
- this.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@Componentstruct Index { //播放的歌曲信息 @StorageLink('song') songList:songInfo[]=[] //播放的歌曲索引 @StorageLink('i') currentIndex:number = -1 aboutToAppear(){ this.songList = song.songList AppStorage.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)//歌曲id Image(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 = avFile
- return 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 = 0
- this.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@Componentstruct 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.AVPlayer
- async 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 = 0 this.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 = avFile
- return avPlayer
- }}
复制代码 总结
使用AVPlayer开发音频播放,重点是掌握怎样获取播放路径(本地地点,网络地点)等,再根据歌曲的状态编写对应函数即可。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |