做自己喜欢又擅长的,同时解决生活中的实际痛点,简单极致、有用有趣,It’s my life.
项目需求
功能要求:一个窗口,一个列表,喜欢的歌曲,支持循环播放/进度拖动/歌词同步显示/动画/异步网络歌曲、图片及歌词下载;
应用场景:徒步、登山、坐享时使用。
设计及实现
1.0版本
2.0版本
歌名应景:平和、坚持、创新
smooth
baby one more time
i want in that way
踩过的坑
多线程
扫描媒体库时,模拟器上文件少未出现异常,但由于真机文件较多,导致主线程阻塞,新增线程执行歌曲扫描。
运行时权限
采用获取并判断IMEI号是否为空或者全0来判断是否为模拟器环境,需要获取到READ_PHONE_STATE权限,运行时动态申请。
异步下载
通过服务实现,下载完成后,发广播通知,并通过附加参数传递文件路径。
歌词解析
歌词文件格式不统一,需要多做兼容性测试,譬如一行有多个时间标签,再如标签中存在异常字符等。
另外,解析出来的歌词节点列表,需要基于时间点排序,
mediaplayer的进度值单位为毫秒,解析歌词时间时,尽量保持单位一致。
实时更新UI
方法一:
Handle的post方法,在post的Runable的run方法中,使用postDelay方法再次post该Runable对象,在Runable中更新UI,达到实时更新UI的目的
方法二:
多开一个线程,线程写一个持续循环,每次进入循环内即post一次Runable,然后休眠1000ms,亦可做到实时更新UI
注意:因为用到了多线程,有可能在执行onDestroy方法之后,Runable的run方法还有可能在继续执行,因此要在onDestroy方法释放资源后置空,在run方法中要判空。
歌曲切换
setDataSource()之前需要调用reSet()方法,才可以重新设置歌曲
中文乱码
歌词文件编码不一致导致,内部需要统一处理
属性动画
界面动画不要太多,以免给用户造成眼花缭乱的不好体验
SeekBar
联动效果,进度/时间/歌词等要同步变动
控制逻辑
响应点击播放/暂停/停止按钮时,要同步控制动画及各关联控件。
浮点运算
根据seekbar当前进度来计算提示时间的横坐标偏移量时,((double)curPos/(double)maxPos) * width
mediaplayer状态机
状态1:Idel(空闲)状态
当 mediaplayer创建或者执行reset()方法后处于这个状态。
状态2:Initialized(已初始化)状态
当调用mediaplayer的setDataResource()方法给mediaplayer设置播放的数据源后,mediaplayer会处于该状态。
状态3:Prepared(准备就续)状态
设置完数据源后,调用mediaplayer的prepare()方法,让mediaplayer准备播放。值得一提的是,这里除了prepare()方法,还有prepareAsnyc()方法,此方法是异步方法,一般用于网络视频的缓冲。当缓冲完毕后,就会触发准备完毕的事件。我们要做的就是监听该事件(OnPreparedListener),当缓冲完成时,执行相应的操作。在此状态上,我们可以调用seekTo()方法定位视频,此方法不改变mediaplayer的状态;亦可调用stop()放弃视频播放,使mediaplayer处于Stopped状态。一般我们会在此状态上调用start()方法开始播放视频。
状态4:Started(开始)状态
当处于Prepared状态、Paused状态和PlayebackCompeleted状态时,调用Started()方法即可进入该状态。在该状态中,mediaplayer开始播放视频,可以通过seekTo()方法和start()方法改变视频播放的进度,当Looping为真且播放完毕后,它会重新开始播放(即循环播放);否则播放完毕后,会触发事件并调用OnCompletionaListener.OnCompletion()方法,进行特定操作,并进入PlaybackCompleted状态。在此状态中,亦可调用pause()方法或者stop()方法让视频暂停或停止,此时mediaplayer分别处于Stopped和Paused状态。
状态5:Stopped(停止)状态
当 mediaplayer处于Prepared、Started、Paused、PlaybackCompleted状态时,调用stop()方法即可进入本状态。应特别注意的是,在本状态中,若想重新开始播放,不能直接调用start()方法,必须调用prepare()方法或prepareAsync()方法重新让mediaplayer处于Prepared状态方可调用start()方法播放视频。
状态6:Paused(暂停)状态
当mediaplayer处于Started状态是,调用pause()方法即可进入本状态。在本状态里,可直接调用start()方法使,mediaplayer回到Started状态,亦可调用stop()方法停止视频播放,让播放器处于停止态。
状态7:PlaybackCompleted(播放完成)状态
当mediaplayer播放完成且Looping为假时即可进入本状态。在本状态可调用start()方法使mediaplayer回到Started状态(注意此时是从头开始播放);亦可调用stop()方法使mediaplayer处于停止态,结束播放。
状态8:Error(错误)状态
当mediaplayer出现错误时处于此状态。
调用release()方法即可释放此mediaplayer对象。
参考资料
MediaPlayer播放音频与视频
Android MediaPlayer和VideoView的使用
Android Supported Media Formats(Android支持的媒体格式)
彻底解决android读取中文txt,lrc的乱码(自动判断文档类型并转码)
Android实现简单音乐播放器(MediaPlayer)
Android应用–简、美音乐播放器原型放送