源码地址:https://github.com/yeaper/MusicPlayer

因项目需要,实现的功能类似QQ音乐播放界面

使用 kotlin 代替 Java

主要功能:

1、播放、暂停音乐

2、自动、手动设置进度条,并且同步播放音乐

3、开启、暂停、停止匀速旋转的动画

先看效果图:

     

1、布局文件

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"xmlns:app="http://schemas.android.com/apk/res-auto"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@color/white"tools:context="cc.redhome.hduin.view.discover.hduradio.programalbum.ProgramPlayActivity"><ImageViewandroid:id="@+id/programPlayHeaderBg"android:layout_width="match_parent"android:layout_height="240dp"android:background="@drawable/hdu_radio_header_bg"/><LinearLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><TextViewandroid:id="@+id/programPlayLyric"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_horizontal"android:gravity="center_horizontal"android:layout_marginTop="80dp"android:layout_marginLeft="25dp"android:layout_marginRight="25dp"android:lineSpacingMultiplier="1.5"android:textColor="@color/white"android:textSize="17sp"/><FrameLayoutandroid:layout_width="140dp"android:layout_height="140dp"android:layout_gravity="center_horizontal"android:layout_marginTop="30dp"><com.pkmmte.view.CircularImageViewandroid:layout_width="match_parent"android:layout_height="match_parent"android:layout_gravity="center"android:src="@drawable/developer_fat"app:shadow="true"app:border="true"app:border_width="9dp"app:border_color="@color/record_border_bg_color" /><com.pkmmte.view.CircularImageViewandroid:id="@+id/programPlayRecordImage"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_gravity="center"android:layout_margin="6dp"/></FrameLayout><TextViewandroid:id="@+id/programPlayName"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_horizontal"android:layout_marginTop="20dp"android:textColor="@color/primaryTextDark"android:textSize="17sp"/><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"><TextViewandroid:id="@+id/programPlayAnchor"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerHorizontal="true"android:layout_marginTop="10dp"android:textColor="@color/primaryTextDark"android:textSize="16sp"/><TextViewandroid:id="@+id/programPlayDirector"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_below="@id/programPlayAnchor"android:layout_alignLeft="@id/programPlayAnchor"android:textColor="@color/primaryTextDark"android:textSize="16sp"/><TextViewandroid:id="@+id/programPlayProducer"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_below="@id/programPlayDirector"android:layout_alignLeft="@id/programPlayAnchor"android:textColor="@color/primaryTextDark"android:textSize="16sp"/></RelativeLayout><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="15dp"android:layout_marginLeft="15dp"android:layout_marginRight="15dp"><TextViewandroid:id="@+id/programPlayStartTime"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentLeft="true"android:layout_centerVertical="true"android:textColor="@color/time_text_color"android:textSize="13sp"android:text="00:00"/><TextViewandroid:id="@+id/programPlayEndTime"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentRight="true"android:layout_centerVertical="true"android:textColor="@color/time_text_color"android:textSize="13sp"/><app.minimize.com.seek_bar_compat.SeekBarCompatandroid:id="@+id/programPlayProgressBar"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerVertical="true"android:layout_toRightOf="@id/programPlayStartTime"android:layout_toLeftOf="@id/programPlayEndTime"android:max="100"android:maxHeight="4dp"android:progressDrawable="@drawable/program_play_seekbar_bg"app:progressBackgroundColor="@color/seekBar_bg_color"app:progressColor="@color/seekBar_progress_color"app:thumbColor="@color/seekBar_progress_color"app:thumbAlpha="1.0"/></RelativeLayout><RelativeLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginTop="30dp"android:layout_marginBottom="20dp"><ImageViewandroid:id="@+id/programPlayStart"android:layout_width="72dp"android:layout_height="72dp"android:layout_centerHorizontal="true"android:src="@drawable/program_play_start"/><ImageViewandroid:id="@+id/programPlayPrevious"android:layout_width="19dp"android:layout_height="19dp"android:layout_toLeftOf="@id/programPlayStart"android:layout_centerVertical="true"android:layout_marginRight="30dp"android:src="@drawable/program_play_previous"/><ImageViewandroid:id="@+id/programPlayNext"android:layout_width="19dp"android:layout_height="19dp"android:layout_toRightOf="@id/programPlayStart"android:layout_centerVertical="true"android:layout_marginLeft="30dp"android:src="@drawable/program_play_next"/></RelativeLayout></LinearLayout><include layout="@layout/toolbar_transparent"/>
</FrameLayout>

主要用到 2 个第三方控件

(1)compile 'com.pkmmte.view:circularimageview:1.1'

(2)compile 'com.minimize.library:seekbar-compat:0.2.5'

其中,program_play_seekbar_bg.XML文件如下:

<?xml version="1.0" encoding="utf-8"?>
<layer-listxmlns:android="http://schemas.android.com/apk/res/android"><item android:id="@android:id/background"><shape><corners android:radius="10dp"/></shape></item><item android:id="@android:id/secondaryProgress"><clip><shape><corners android:radius="10dp"/></shape></clip></item><item android:id="@android:id/progress"><clip><shape><corners android:radius="10dp"/></shape></clip></item>
</layer-list>

2、后台播放、暂停音乐服务类

class MusicPlayerService : Service() {private var mediaPlayer: MediaPlayer? = nulloverride 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 = 0if (mediaPlayer != null) {rtn = mediaPlayer!!.duration}return rtn}//获取当前播放进度fun getMusicCurrentPosition(): Int {var rtn = 0if (mediaPlayer != null) {rtn = mediaPlayer!!.currentPosition}return rtn}fun seekTo(position: Int) {if (mediaPlayer != null) {mediaPlayer!!.seekTo(position)}}}
}

3、音乐播放类

class ProgramPlayActivity : BaseActivity() {var actionBar: ActionBar? = nullvar pastProgram: PastProgram? = nullvar serviceConnection: ServiceConnection? = nullprivate var binder: MusicPlayerService.MyBinder? = nullvar isFinished = false // 是否结束当前activity的标志var isPlaying = falseprivate var currentValue = 0fprivate var objAnim: ObjectAnimator? = nulloverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_program_play)initToolbar()initInfo()}fun initToolbar(){setSupportActionBar(toolbar)actionBar = supportActionBarif (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!!.namePicasso.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!!.nameprogramPlayAnchor.text = "主播:罗焓智"programPlayDirector.text = "导播:"+pastProgram!!.directorprogramPlayProducer.text = "监制:"+pastProgram!!.producerinitRotateAnim()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 = 3handler.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 = 1msg1.arg1 = binder!!.getMusicCurrentPosition()handler.sendMessage(msg1)// 改变起始时间val msg2 = handler.obtainMessage()msg2.what = 2msg2.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 = trueprogramPlayStart.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 = falseprogramPlayStart.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 = falseprogramPlayStart.setImageResource(R.drawable.program_play_start)programPlayProgressBar.progress = 0programPlayStartTime.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()// 停止更新UIisFinished = true// 关闭播放器,解绑服务val intent = Intent(this, MusicPlayerService::class.java)intent.putExtra("action", "release")intent.putExtra("musicUrl", pastProgram!!.audioUrl)startService(intent)unbindService(serviceConnection)}
}

有具体问题,可以留言讨论!!

仿QQ音乐播放界面(已实现主要功能)相关推荐

  1. 基于jQuery仿QQ音乐播放器网页版代码

    基于jQuery仿QQ音乐播放器网页版代码是一款黑色样式风格的网页QQ音乐播放器样式代码.效果图如下: 在线预览    源码下载 实现的代码. html代码: <div class=" ...

  2. 20230621----重返学习-仿QQ音乐播放器-静态页面的免费部署-vue2

    day-096-ninety-six-20230621-仿QQ音乐播放器-静态页面的免费部署-vue2 仿QQ音乐播放器 audio音频标签 audio标签 <audio src="i ...

  3. iOS开发手记-仿QQ音乐播放器动态歌词的实现

    最近朋友想做个音乐App,让我帮忙参考下.其中歌词动态滚动的效果,正好我之前也没做过,顺便学习一下,先来个预览效果. 实现思路 歌词常见的就是lrc歌词了,我们这里也是通过解析lrc歌词文件来获取其播 ...

  4. jQuery仿QQ音乐播放器

    本文通过Html+CSS+jQuery开发仿QQ版的音乐播放器,是前端技术的综合应用,所用素材来源于网络,仅供学习分享使用,如有不足之处,还请指正. 涉及知识点 在本例中用到的知识点如下,按jQuer ...

  5. zepto+less写QQ音乐播放界面,进度条同步,歌词同步高亮等等(带注释,可参考可直接使用)

    这是效果图,小编截下来的是静态图片,真是都是可以动的 不多说,直接上代码,注释都写在代码里清清楚楚 less @sizeMan:100%; //关键帧动画函数 .keyframes(@name:mov ...

  6. ios音乐播放器-仿QQ音乐

    这篇文章主要写一个iOS系统下的音乐播放器 , 包括简单的仿QQ音乐播放器界面.音乐播放.歌词解析.后台控制等  ,如果你正好需要 , 希望你看完后能够对你的提升有所帮助 , 当然,阅读中如果发现什么 ...

  7. android qq音乐布局,仿QQ音乐底部栏

    最近在开发一款高仿QQ音乐播放器的Demo,遇到了一个问题,在QQ音乐主界面有一个常驻底部栏,底部栏中有一个可左右滑动切歌的组件,最后还是实现了效果,今天来回顾一下实现过程. 要实现的就是最下方的常驻 ...

  8. Qt5学习 模仿qq音乐播放器样式(2)——点击动画效果+歌词颜色变换展示

    拖的太久,主要再上一篇文章中,新学习了相关知识,做了右键菜单,点击按钮动画切换窗口和播放时歌词颜色显示当前播放位置. 主要为了实现功能的展示,所以很多文件读取都直接采用了本地文件这种比较low的方式. ...

  9. 仿酷狗音乐播放器已开源!

    转载请说明原出处,谢谢:http://blog.csdn.net/zhuhongshu/article/details/41037875 距离我发布测试版的Redrain音乐盒(仿酷狗播放器),现在正 ...

最新文章

  1. python导入数据画多列直方图_在python datafram中使用两列(值、计数)绘制直方图...
  2. C/C++ 日期 时间 time_t与struct tm转换 收藏
  3. 16位汇编 call调用函数 通过栈来传递参数
  4. Python进程multiprocessing. Process()的使用
  5. matlab创建数组对象,MATLAB一维数组(向量)的定义
  6. http请求头中Origin的作用及危害
  7. vrm华为_华为-笔记本电脑如何安装FusionCompute虚拟化平台?
  8. asp.net中读取数据库中的数据可以使用DataReader和DataSet 2种方式(初学者望大家不要笑我)...
  9. 中标麒麟 V7 操作系统安装达梦数据库 DM8
  10. 计算机和未来汽车有联系吗,汽车的未来是会奔跑的计算机?
  11. Oracle 对表空间无操作权限
  12. 《拆掉思维里的墙》读书笔记
  13. java 23种设计模式 04 单例模式
  14. 基于LM334芯片的恒流源调试
  15. mysql_如果字段null,则替换
  16. 手把手教你写一个基于python+pyqt5的股票盯盘软件
  17. VC++流量监控程序源代码
  18. 内网渗透之Windows 系统下的目标信息收集
  19. “使能”数字化 “浙”里很精彩
  20. POi 常见读写Excel 及格式设置

热门文章

  1. 校园O2O商铺平台-商品模块
  2. SSM + MySQL 服装商城
  3. truncate(截断)与delete(删除)的区别
  4. 胜为蓝牙适配器驱动_胜为udc 324b蓝牙驱动程序
  5. 并行工程的本质分析(转)
  6. 能源互联网计量柜系统
  7. Python调用剪切板的几种方法
  8. 求威纶触摸屏i系列HMI USB driver 驱动程序和安装说明
  9. FRDM-KW36入门学习(一、IAR环境搭建)
  10. google scholar按照 引用量进行排序 harzing Publish or Perish