源码地址:https://github.com/yeaper/MusicPlayer
因项目需要,实现的功能类似QQ音乐播放界面
使用 kotlin 代替 Java
主要功能:
1、播放、暂停音乐
2、自动、手动设置进度条,并且同步播放音乐
3、开启、暂停、停止匀速旋转的动画
先看效果图:
1、布局文件
主要用到 2 个第三方控件
(1)compile 'com.pkmmte.view:circularimageview:1.1'
(2)compile 'com.minimize.library:seekbar-compat:0.2.5'
其中,program_play_seekbar_bg.XML文件如下:
-
-
-
2、后台播放、暂停音乐服务类
class MusicPlayerService : Service() {
private var mediaPlayer: MediaPlayer? = null
override fun onBind(p0: Intent?): IBinder {
return MyBinder()
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
var action = ""
var musicUrl = ""
//获取意图传递的信息
if(intent != null){
action = intent.getStringExtra("action")
musicUrl = intent.getStringExtra("musicUrl")
}
when (action) {
"prepare" -> {
if (mediaPlayer == null) {
mediaPlayer = MediaPlayer()
mediaPlayer!!.setAudioStreamType(AudioManager.STREAM_MUSIC)
mediaPlayer!!.setDataSource(musicUrl)
mediaPlayer!!.prepare()
}
}
"play" -> {
if (mediaPlayer == null) {
mediaPlayer = MediaPlayer()
mediaPlayer!!.setAudioStreamType(AudioManager.STREAM_MUSIC)
mediaPlayer!!.setDataSource(musicUrl)
mediaPlayer!!.prepare()
}
if (mediaPlayer != null && !mediaPlayer!!.isPlaying) {
mediaPlayer!!.start()
}
}
"pause" -> {
if (mediaPlayer != null && mediaPlayer!!.isPlaying) {
mediaPlayer!!.pause()
}
}
"stop" -> {
if (mediaPlayer != null && mediaPlayer!!.isPlaying) {
mediaPlayer!!.stop()
}
}
"release" -> {
if (mediaPlayer != null) {
mediaPlayer!!.stop()
mediaPlayer!!.release()
mediaPlayer = null
}
}
}
return super.onStartCommand(intent, flags, startId)
}
internal inner class MyBinder : Binder() {
//获取歌曲长度
fun getMusicDuration(): Int {
var rtn = 0
if (mediaPlayer != null) {
rtn = mediaPlayer!!.duration
}
return rtn
}
//获取当前播放进度
fun getMusicCurrentPosition(): Int {
var rtn = 0
if (mediaPlayer != null) {
rtn = mediaPlayer!!.currentPosition
}
return rtn
}
fun seekTo(position: Int) {
if (mediaPlayer != null) {
mediaPlayer!!.seekTo(position)
}
}
}
}
3、音乐播放类
class ProgramPlayActivity : BaseActivity() {
var actionBar: ActionBar? = null
var pastProgram: PastProgram? = null
var serviceConnection: ServiceConnection? = null
private var binder: MusicPlayerService.MyBinder? = null
var isFinished = false // 是否结束当前activity的标志
var isPlaying = false
private var currentValue = 0f
private var objAnim: ObjectAnimator? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_program_play)
initToolbar()
initInfo()
}
fun initToolbar(){
setSupportActionBar(toolbar)
actionBar = supportActionBar
if (actionBar != null) {
// 显示返回按钮
actionBar!!.setDisplayHomeAsUpEnabled(true)
// 隐藏 ActionBar 自带标题
actionBar!!.setDisplayShowTitleEnabled(false)
}
}
fun initInfo(){
if(intent.extras != null){
pastProgram = intent.getSerializableExtra("program") as PastProgram?
}
toolbar_title.text = pastProgram!!.name
Picasso.with(this)
.load(pastProgram!!.bgImageUrl)
.placeholder(R.drawable.hdu_radio_header_bg)
.into(programPlayHeaderBg)
Picasso.with(this)
.load(pastProgram!!.bgImageUrl)
.placeholder(R.drawable.developer_fat)
.into(programPlayRecordImage)
programPlayLyric.text = "The truth that you leave-Pianoboy\n说了再见以后-苏打绿"
programPlayName.text = pastProgram!!.name
programPlayAnchor.text = "主播:罗焓智"
programPlayDirector.text = "导播:"+pastProgram!!.director
programPlayProducer.text = "监制:"+pastProgram!!.producer
initRotateAnim()
prepareMediaPlayer()
setListener()
}
/**
* 准备播放器
*/
fun prepareMediaPlayer(){
val intent = Intent(this, MusicPlayerService::class.java)
intent.putExtra("action", "prepare")
intent.putExtra("musicUrl", pastProgram!!.audioUrl)
startService(intent)
if (serviceConnection == null) {
serviceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName, service: IBinder) {
binder = service as MusicPlayerService.MyBinder
// 设置进度条的最大长度
programPlayProgressBar.max = binder!!.getMusicDuration()
// 设置歌曲总时长
programPlayEndTime.text = msecToPlayTime(binder!!.getMusicDuration())
programPlayProgressBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
// 手动控制进度
if(fromUser){
binder!!.seekTo(progress)
}
// 播放结束后,还原状态
if(progress == seekBar.max){
binder!!.seekTo(0)
val msg = handler.obtainMessage()
msg.what = 3
handler.sendMessage(msg)
}
}
override fun onStartTrackingTouch(seekBar: SeekBar) {
}
override fun onStopTrackingTouch(seekBar: SeekBar) {
}
})
// 连接之后启动子线程设置当前进度
object : Thread() {
override fun run() {
while (true) {
if(isFinished){
break
}
// 改变当前进度条的值
val msg1 = handler.obtainMessage()
msg1.what = 1
msg1.arg1 = binder!!.getMusicCurrentPosition()
handler.sendMessage(msg1)
// 改变起始时间
val msg2 = handler.obtainMessage()
msg2.what = 2
msg2.obj = msecToPlayTime(binder!!.getMusicCurrentPosition())
handler.sendMessage(msg2)
try {
Thread.sleep(100)
} catch (e: Exception) {
e.printStackTrace()
}
}
}
}.start()
}
override fun onServiceDisconnected(name: ComponentName) {
}
}
// 以绑定方式连接服务
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)
}
}
fun setListener(){
programPlayStart.setOnClickListener {
playPause()
}
programPlayPrevious.setOnClickListener {
}
programPlayNext.setOnClickListener {
}
}
private var handler = object : Handler() {
override fun handleMessage(msg: Message?) {
when(msg!!.what){
1 -> {
programPlayProgressBar.progress = msg.arg1
}
2 -> {
programPlayStartTime.text = msg.obj as String
}
3 -> {
resetPlay()
}
}
}
}
/**
* 开启动画
*/
fun startAnimation() {
// 设置动画,从上次停止位置开始,这里是顺时针旋转360度
objAnim = ObjectAnimator.ofFloat(programPlayRecordImage, "Rotation",
currentValue - 360, currentValue)
// 设置持续时间
objAnim!!.duration = 30000
// 设置循环播放
objAnim!!.repeatCount = ObjectAnimator.INFINITE
// 设置匀速播放
val lin: LinearInterpolator = LinearInterpolator()
objAnim!!.interpolator = lin
// 设置动画监听
objAnim!!.addUpdateListener({ animation ->
// 监听动画执行的位置,以便下次开始时,从当前位置开始
currentValue = animation.animatedValue as Float
})
objAnim!!.start()
}
/**
* 停止动画
*/
fun stopAnimation() {
objAnim!!.end()
currentValue = 0f // 重置起始位置
}
/**
* 暂停动画
*/
fun pauseAnimation() {
objAnim!!.cancel()
}
/**
* 播放、暂停音乐
*/
fun playPause(){
if(!isPlaying){
isPlaying = true
programPlayStart.setImageResource(R.drawable.program_play_pause)
// 开启图片旋转动画
startAnimation()
val intent = Intent(this, MusicPlayerService::class.java)
intent.putExtra("action", "play")
intent.putExtra("musicUrl", pastProgram!!.audioUrl)
startService(intent)
}else {
isPlaying = false
programPlayStart.setImageResource(R.drawable.program_play_start)
// 暂停动画
pauseAnimation()
val intent = Intent(this, MusicPlayerService::class.java)
intent.putExtra("action", "pause")
intent.putExtra("musicUrl", pastProgram!!.audioUrl)
startService(intent)
}
}
/**
* 还原到音乐起始状态
*/
fun resetPlay(){
isPlaying = false
programPlayStart.setImageResource(R.drawable.program_play_start)
programPlayProgressBar.progress = 0
programPlayStartTime.text = "00:00"
// 关闭动画
stopAnimation()
val intent = Intent(this, MusicPlayerService::class.java)
intent.putExtra("action", "pause")
intent.putExtra("musicUrl", pastProgram!!.audioUrl)
startService(intent)
}
/**
* 毫秒转换为播放时间
*/
fun msecToPlayTime(time: Int): String{
var min = time.div(60000).toString()
var second = time.mod(60000).div(1000).toString()
if(min.toInt() < 10){
min = "0"+min
}
if(second.toInt() < 10){
second = "0"+second
}
return min+":"+second
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.toolbar_transport_menu, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when(item.itemId){
android.R.id.home -> { finish() }
R.id.program_play_music_list -> {}
}
return super.onOptionsItemSelected(item)
}
override fun onDestroy() {
super.onDestroy()
// 停止更新UI
isFinished = true
// 关闭播放器,解绑服务
val intent = Intent(this, MusicPlayerService::class.java)
intent.putExtra("action", "release")
intent.putExtra("musicUrl", pastProgram!!.audioUrl)
startService(intent)
unbindService(serviceConnection)
}
}
有具体问题,可以留言讨论!!