美文网首页安卓定义控件Multimedia
Android开源在线音乐播放器——波尼音乐

Android开源在线音乐播放器——波尼音乐

作者: 王晨彦 | 来源:发表于2016-06-08 10:56 被阅读17877次

波尼音乐

image

系列文章

前言

毕业设计做的项目,答辩完了,就共享出来。

简介

波尼音乐是一款开源Android在线音乐播放器。

  • 播放本地音乐与在线音乐
  • 在线音乐排行榜,如热歌榜、新歌榜等
  • 高仿云音乐的黑胶唱片专辑封面
  • 歌词显示,自动搜索歌词
  • 编辑歌曲信息
  • 夜间模式
  • 定时关闭

更新说明

v 1.3.0

  • 新增歌词支持上下拖动
  • 新增支持分屏模式
  • 新增本地歌曲支持按大小和时长过滤
  • 新增下载的歌曲文件自动添加专辑封面
  • 新增编辑歌曲信息
  • 新增5.0以上系统支持联动系统媒体中心,锁屏显示播放信息
  • 修复已知bug

v 1.2.3

  • 新增通知栏播放控制
  • 修复魅族手机扫描不到音乐的问题
  • 修复已知bug

v 1.2.0

  • 修复在线音乐无法加载的问题
  • 修复弱网时播放网络歌曲导致ANR的问题
  • 修复每日启动图片无法更新的问题
  • 下载在线歌曲可以显示专辑封面了
  • 修复已知bug

v 1.1.0

  • 支持 Android 6.0 运行时权限
  • 修复已知bug

v 1.0.0

  • First Release

下载地址

蒲公英:https://www.pgyer.com/ponymusic

TODO

  • [ ] 在线音乐可以免下载加入我的音乐列表
  • [ ] 在线音乐自动缓存
  • [x] 编辑音乐信息

项目

公开API

开源技术

关键代码

黑胶唱片专辑封面绘制流程

@Override
protected void onDraw(Canvas canvas) {
    // 1.绘制顶部虚线
    mTopLine.setBounds(0, 0, getWidth(), mTopLineHeight);
    mTopLine.draw(canvas);
    // 2.绘制黑胶唱片外侧半透明边框
    mCoverBorder.setBounds(mDiscPoint.x - mCoverBorderWidth, mDiscPoint.y - mCoverBorderWidth,
            mDiscPoint.x + mDiscBitmap.getWidth() + mCoverBorderWidth, mDiscPoint.y +
                    mDiscBitmap.getHeight() + mCoverBorderWidth);
    mCoverBorder.draw(canvas);
    // 3.绘制黑胶
    // 设置旋转中心和旋转角度,setRotate和preTranslate顺序很重要
    mDiscMatrix.setRotate(mDiscRotation, mDiscCenterPoint.x, mDiscCenterPoint.y);
    // 设置图片起始坐标
    mDiscMatrix.preTranslate(mDiscPoint.x, mDiscPoint.y);
    canvas.drawBitmap(mDiscBitmap, mDiscMatrix, null);
    // 4.绘制封面
    mCoverMatrix.setRotate(mDiscRotation, mCoverCenterPoint.x, mCoverCenterPoint.y);
    mCoverMatrix.preTranslate(mCoverPoint.x, mCoverPoint.y);
    canvas.drawBitmap(mCoverBitmap, mCoverMatrix, null);
    // 5.绘制指针
    mNeedleMatrix.setRotate(mNeedleRotation, mNeedleCenterPoint.x, mNeedleCenterPoint.y);
    mNeedleMatrix.preTranslate(mNeedlePoint.x, mNeedlePoint.y);
    canvas.drawBitmap(mNeedleBitmap, mNeedleMatrix, null);
}

歌词绘制流程

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    // 中心Y坐标
    float centerY = getHeight() / 2 + mTextSize / 2 + mAnimOffset;

    // 无歌词文件
    if (!hasLrc()) {
        float centerX = (getWidth() - mCurrentPaint.measureText(label)) / 2;
        canvas.drawText(label, centerX, centerY, mCurrentPaint);
        return;
    }

    // 画当前行
    String currStr = mLrcTexts.get(mCurrentLine);
    float currX = (getWidth() - mCurrentPaint.measureText(currStr)) / 2;
    canvas.drawText(currStr, currX, centerY, mCurrentPaint);

    // 画当前行上面的
    for (int i = mCurrentLine - 1; i >= 0; i--) {
        String upStr = mLrcTexts.get(i);
        float upX = (getWidth() - mNormalPaint.measureText(upStr)) / 2;
        float upY = centerY - (mTextSize + mDividerHeight) * (mCurrentLine - i);
        // 超出屏幕停止绘制
        if (upY - mTextSize < 0) {
            break;
        }
        canvas.drawText(upStr, upX, upY, mNormalPaint);
    }

    // 画当前行下面的
    for (int i = mCurrentLine + 1; i < mLrcTimes.size(); i++) {
        String downStr = mLrcTexts.get(i);
        float downX = (getWidth() - mNormalPaint.measureText(downStr)) / 2;
        float downY = centerY + (mTextSize + mDividerHeight) * (i - mCurrentLine);
        // 超出屏幕停止绘制
        if (downY > getHeight()) {
            break;
        }
        canvas.drawText(downStr, downX, downY, mNormalPaint);
    }
}

截图

image
image
image
image
image
image

关于作者

简书:http://www.jianshu.com/users/3231579893ac

微博:http://weibo.com/wangchenyan1993

License

Copyright 2016 wangchenyan

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

相关文章

网友评论

  • 彡Panda彡:您好,github代码database中Musicdao是否丢失
    王晨彦:@彡Panda彡 这个类是greenDao自动生成的,build一下就有了
  • plus彭于晏:为什么我扫描本地音乐,第一次进入时一直在显示正在扫描,第二次进入就很快扫描到了
  • 麦香菌:音乐数据是用的网易云的吗,求音乐接口
  • 我一定会学会:大佬请问一下,你这个音乐是如果做缓存的,是有先下载下来吗,还是mediaplay自带缓存?(需求是没播过网络请求,播过就本地读取)
  • _动力火车:那个为啥 res/values/styles.xml 会报错啊。error: Error: No resource found that matches the given name: attr 'colorPrimary'. 这个为啥啊 难道是版本问题??
    14e923654e52:我也是这个问题,不知道有没有解决方法?
  • b349e30ba5e5:这软件的数据库是什么,作用是什么,是用来对于该软件内的歌曲储存?那搜索里的歌曲是哪来的,就是关键字搜索出来的歌曲,还有就是里面的DBmanager.java是用来干嘛的
  • 14e57d9aa68a:在线音乐播放了并不能在本地音乐找到啊
  • 14e57d9aa68a:要播放在线音乐是要先下载吗?下载了放在哪里,一个项目有数据库吗
  • 14e57d9aa68a:我想问下Appcache是用来做什么的,还有longsparsearray的作用是什么
  • 趔趄徒成玦:你好,请问搜索QQ音乐曲库的Http请求怎么写?
  • 11985abefa30:你好 我加载一个音频的图片时候一直是这个错误
    E/ loadCoverFromFile TAG: PATH http://img.taopic.com/uploads/allimg/120727/201995-120HG1030762.jpg
    E/BitmapFactory: Unable to decode stream: java.io.FileNotFoundException: http:/img.taopic.com/uploads/allimg/120727/201995-120HG1030762.jpg: open failed: ENOENT (No such file or directory)
    图片地址是没有错呀 是什么原因呢
    11985abefa30:@ChayWong 找到了 谢谢提点
    网络音频如何实现按顺序播放呢 有什么思路嘛
    11985abefa30:@ChayWong Demo里播放网络音频时 图片的加载是在哪一块呢
    王晨彦:@Crush_588b 因为这个是网络图片,BitmapFactory只能加载本地图片。
  • 2a759147be86:播放网络音乐的时候,如果多个音乐连续切换点击的话就会造成ANR。这个问题我的项目里面也存在,但是还没有找到合适的解决方案。
    王晨彦:@375_5a6a 感谢提醒!
    王晨彦:@Crush_588b 你好,目前还没有实现这个功能。
    11985abefa30:播放网络音频 能实现按列表顺序播放嘛?
  • 11985abefa30:你好 请问一下在线音乐 怎么实现播放完自动播放下一首 列表顺序播放 谢谢!
  • c6784f4e55f0:你好,我想问一下,音乐的专辑封面缩略图我从手机里删除后,打开我的播放器就不显示了,你播放器打开后又生成了缩略图,这个是怎么实现的呢?
  • 王晨彦:@bigtreee 需要配置高德定位apikey
    plus彭于晏:我配置了apikey,为什么还是不能显示,打印出获取天气预报失败,也没看到哪里是获取天气预报数据的
    13af09417604:@ChayWong 点击夜间模式时,app会崩溃,调试发现lvLocalMusic和lvPlaylist都为null
    13af09417604:@ChayWong 已配置高德apikey,完美解决天气问题,感谢!
  • 13af09417604:非常好的项目,辛苦了,顶顶!
    想咨询个问题?为什么我Git下载源码后用Android Stutio 3.0编译后(什么也没有修改),天气不显示(debug提示:获取天气预报失败,跟踪发现“定位失败无法获取城市信息”,
    下载您提供的APP(fir.im:http://fir.im/ponymusic)可以正常显示天气
  • 骑驴去看海:很厉害
  • 彼岸青园:大佬,好厉害~
  • 787ced935c4a:博主大大 你好, 我看到你写的APP介绍中有提到 在线缓存音乐! 这个是咋实现的? 我看了下源码 没有发现这块的逻辑啊!!! 我现在就有这个需求 需要缓存音频!
    787ced935c4a:@ChayWong 请问下博主,有啥思路么? 可否提供一下你的思路! 我在网上找了 都没啥效果···
    王晨彦: @灼灼其华s 这个功能是在TODO list中的,还未实现,抱歉。
    787ced935c4a:还有博主, 我听完一首歌了, 然后我断开网络回去听, 就不能听了!
  • DylanW:请教一下,运行后天气信息这一块是空的,是什么原因呢?
    DylanW:@ChayWong 是不是AMAP_KEY这里出问题了?
    DylanW:@ChayWong 应用权限中位置项已打开,另外gps也打开了。还需要什么设置吗
    王晨彦: @DylanW 可能是因为没有定位权限。
  • d8ceff4eefc6:在线音乐怎么播放完自动播放下一首 列表顺序播放
    王晨彦: @jaioyangzz 目前还没有实现这个功能
  • 8706d7ec7fe0:感谢分享:smile:
  • 孔雀叔:rxjava可以从1升到2了
    王晨彦: @孔雀叔 我有空升级一下
    孔雀叔:@ChayWong 是的,我最近在看你的项目,应该是天气,弄错文章了。从1升到2,改动太大,我正在尝试修改。
    王晨彦:@孔雀叔 感谢你的建议,但是这个项目没有用到rxjava,你说的时天气这个应用吗?
  • 秋天的田野:有一个BUG,下载的音乐当删除app后音乐也被删除了
    秋天的田野:@秋天的田野 点击下载,弹出提示正在下载,然后就没反应了
    秋天的田野:@ChayWong 现在又下载不了了,华为荣耀8青春版 7.0系统
    王晨彦: @秋天的田野 不会的,删除后下载的歌曲还在的
  • f71da46e49da:代码写的好工整啊,受教了,在耳机线控功能这块,是怎么识别按键的呢,我把RemoteControlRceiver 里的action_up改成了action_down,试了一下,还是按键抬起来后才能暂停或者播放,请问这块是为什么呢?
  • twohuoone:你好,我最近也在做播放器,我的播放器是通过activity里面生成的,不是服务,这样就会导致加载中的时候我无法控制了然后碰到一个问题,网速很慢的情况下,一直在加载,请问你有什么好办法让我能在加载时间很长以后直接停止加载,据我现在了解到的只有一个加载完成的监听,看到希望能回复我,困扰我一段时间了
    王晨彦:@twohuoone 用reset就可以吧
    twohuoone:@ChayWong 有强行停止的方法么,不会直接释放mediaplayer吧
    王晨彦:@twohuoone 可以在异步加载开始时启动一个定时任务,到一定时间还没加载完成就强行停止,希望能帮到你。
  • 丶dtdsn:可否告知一下在线歌词的api?
    丶dtdsn: @ChayWong 找了好久没有找到。。
    王晨彦:你好,源码里面有的,麻烦自己看一下。
  • e8150724b0f3:在研究你的代码,可是今天运行APP加载不出网络音乐列表 也无法搜索 请问为什么
    93101642d696:电脑浏览器能访问 http://tingapi.ting.baidu.com/v1/restserver/ting?from=android&version=5.6.5.0&method=baidu.ting.search.merge&format=json&query=
    青花瓷&page_no=1&page_size=50&type=-1&data_source=0&use_cluster=1
    上面是搜素歌曲“”青花瓷”的链接,但是今天在程序中用android OkHttp访问不到(大前天还可以呢)。用手机上的浏览器却可以访问,不能说这个链接无效,所以好奇怪啊
    93101642d696:我今天在线音乐也上不去。如果将代码中的URL拷贝放到浏览器上,就可以访问到数据,用android studio连手机,同样的url,手机就是不能访问到,请问是为什么呢?
    e8150724b0f3:是api接口的问题嘛
  • 007b89c4d4ca:亲,我想研究一下你的代码,不知如何下载看不到下载页面啊
    wmd看海:@Skypew 找到问题的原因了没有,提示play service is null
    Skypew:点击在线音乐 程序崩溃了
    王晨彦:文首有GitHub地址
  • 876f3f66cee2:....有问题,初始化PlayFragment的时候会加载歌词,ui线程内有文件操作,导致耗时过高,ui线程阻塞,会很卡,然后我想改一下,结果发现你的LrcView居然把读取歌词和UI显示放在了一起,然后还是compile形式给出的,根本没法改,需要修正下。 :yum:
    876f3f66cee2:@ChayWong 第一次加载完了,你试一试在PlayFragment里面切换歌曲,还是很卡,你切歌的时候会一直报,Suspending all threads took:,我找到了你的LrcView源码改了一下,把歌词加载放在AsyncTask里面做了,这样打开Fragment的时候主线程就不卡了,可是加载歌词的线程还是很卡,还是一直报Suspending all threads took,我估计是你加载歌词是用List保存的,内存占用过大。。
    王晨彦:@才让小绝 第一次打开播放界面不是因为加载歌词卡,而是因为播放界面Fragment是懒加载,第一次打开会加载Fragment,因此有点卡,不信你可以打开播放界面后切换下歌曲看会不会卡。如果要解决第一次打开卡的问题,可以在启动时就把Fragment加载进来,并隐藏起来。
  • 逆流而鱼:大神我仿照你的夜间模式切换通过修改uiMode来切换夜间模式,res中给夜间模式的资源添加-night后缀,比如values-night,drawable-night这都给了,可就是实现不了效果,大神能解答下吗
    ?在线等,急
  • IT5:你好!我把你的项目导入到我的IDE里面value文件夹下面的style.xml文件有一行报错了,是不是我的版本太低了。我是一Android菜鸟,最近才了解Android一些知识的。
    14e923654e52:我也是,怎么办,在线等?
  • 4d2600b0f244:有个Bug 不能忍, 就是刚打开APP,先不要点底部栏!! 直接取点网络歌曲,随便哪一首,然后回来再点底部栏弹出,会崩掉。 貌似LRC没加载到。
    王晨彦:@4d2600b0f244 你好,这个bug现在已经修复,麻烦你pull下代码,感谢你的反馈!
  • ae12:sorry,i coming again
    我见过 : 布尔表达式?值 0 :值 1,但是大神你下面的代码 xx?xx? a:b:c形式:
    TextUtils.isEmpty(mJOnlineMusic.getPic_big()) ? TextUtils.isEmpty(mJOnlineMusic.getPic_small()) ? null : mJOnlineMusic.getPic_small() : mJOnlineMusic.getPic_big();
    在下第一次见,实在是Google都不知道该怎么google 了,您要是有看到了,就把这个和上面2个问题一起回答一下好吗?在下感激不尽
  • ae12:您好,最近一直在看你的波尼音乐,也在模着写,里面有些代码实在费解,比如上一次留言里的问题,还有,这个
    if (TextUtils.isEmpty(mJOnlineMusic.getLrclink()) || lrcFile.exists()) {
    mCounter++;
    }
    if (TextUtils.isEmpty(mJOnlineMusic.getLrclink()) || lrcFile.exists()) {
    mCounter++;
    }
    if (mCounter == 3) {
    onSuccess(music);
    }
    为什么要满足了mCounter == 3,才可以Play()??
    其中逻辑,没有明白,您能否连着上次的留言和这次的一起回答??
    嗯,有个小请求,能否QQ联系一下,因为这样问问题太不方便了
    我的QQ:2318241264
  • ae12:请问,在PlayService() 里,
    public int play(int position) {
    if (getMusicList().isEmpty()) {
    return -1;
    }

    if (position < 0) {
    position = getMusicList().size() - 1;
    } else if (position >= getMusicList().size()) {
    position = 0;
    }你能大概说一下,里面代码什么意思吗?实在没看懂
    4d2600b0f244:@Liqing_1938 判断歌曲是否为空, 再判断,位置是否小于0,是的话,播放列表最后一首歌,否则播放第一首。
  • zxsong:高仿网易云音乐啊! :+1:
  • ae12:请问,BaseActivity是什么作用?
    王晨彦:@Liqing_1938 启动时会检查splash图是否有更新,有更新的话会下载新的。
    ae12:@ChayWong 恩,谢谢啦.还有个问题,我在程序里加了Log.d()来看程序运行的流程,发现在刚加载MusicActivity()的时候,就开始调用JsonCallback()从网络获取资源解析Json了,但一直不知道怎么实现的?麻烦你再回答一下吧。(新手学习,请多包容)
    王晨彦:@Liqing_1938 BaseActivity主要作用是初始化一些通用操作,如沉浸状态栏,BindView,设置ToolBar等。
  • 小张呵呵:谢谢,我项目正需要使用
  • l誉l:赞 博主 满满的干货
  • ae12:好的,谢谢 :kissing_heart:
    王晨彦:@Liqing_1938 不客气 :smile:
  • ae12:已经在你的GitHub上star了。我把代码运行的时候,其中import me.wcy.music.api.ApiKey;飘红,我猜想:因为我没有申请api吗?请问该怎么解决?
    王晨彦:@Liqing_1938 注意看README哈,ApiKey里面只是检查更新需要的key,我没有提交,直接用空字符串代替就行。

本文标题:Android开源在线音乐播放器——波尼音乐

本文链接:https://www.haomeiwen.com/subject/bxrjdttx.html