android音乐播放器开发 SweetMusicPlayer 智能匹配本地歌词
来源:互联网 发布:淘宝直通车开通流程 编辑:程序博客网 时间:2024/04/30 22:04
上一篇写了使用MediaPlayer播放音乐,http://blog.csdn.net/huweigoodboy/article/details/39861539。
代码地址:https://github.com/huweigoodboy/SweetMusicPlayer
现在来将一下加载本地歌词。好了,还是用那张图。
一,从内存卡上匹配歌词
将会从以下路径匹配
1) SweetMusicPlayer/Lyrics/
2) 歌曲同级目录下
3) 歌曲父级目录/lryics(Lryic加不加s,首字母大小与否又分情况)
LrcContent
package com.huwei.sweetmusicplayer.models;public class LrcContent {private String lrcStr;//歌词内容private int lrcTime;//当前歌词时间public String getLrcStr() {return lrcStr;}public void setLrcStr(String lrcStr) {this.lrcStr = lrcStr;}public int getLrcTime() {return lrcTime;}public void setLrcTime(int lrcTime) {this.lrcTime = lrcTime;}}LrcProcess
package com.huwei.sweetmusicplayer.models;import java.io.BufferedReader;import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;import java.io.InputStreamReader;import java.io.UnsupportedEncodingException;import java.util.ArrayList;import java.util.Collections;import java.util.List;import android.util.Log;import com.huwei.sweetmusicplayer.comparator.LrcComparator;import com.huwei.sweetmusicplayer.datamanager.MusicManager;import com.huwei.sweetmusicplayer.util.OnlineLrcUtil;import com.huwei.sweetmusicplayer.util.TimeUtil;public class LrcProcess {private List<LrcContent> lrclists;public LrcProcess() {super();lrclists = new ArrayList<LrcContent>();lrclists.clear();}public String loadLrc(Song song) {String path = song.getPath();StringBuffer stringBuffer = new StringBuffer();// 得到歌词文件路径String lrcPathString = path.substring(0, path.lastIndexOf("."))+ ".lrc";int index = lrcPathString.lastIndexOf("/");String parentPath;String lrcName;// if(index!=-1){parentPath = lrcPathString.substring(0, index);lrcName = lrcPathString.substring(index);// }File file = new File(lrcPathString);// 匹配SweetMusicPlayer/Lyricsif (!file.exists()) {file = new File(OnlineLrcUtil.getInstance().getLrcPath(song.getTitle(), song.getArtist()));}Log.i("Path", file.getAbsolutePath().toString());// 匹配Lyricsif (!file.exists()) {file = new File(parentPath + "/../" + "Lyrics/" + lrcName);}Log.i("Path", file.getAbsolutePath().toString());// 匹配lyricif (!file.exists()) {file = new File(parentPath + "/../" + "lyric/" + lrcName);}Log.i("Path", file.getAbsolutePath().toString());// 匹配Lyricif (!file.exists()) {file = new File(parentPath + "/../" + "Lyric/" + lrcName);}Log.i("Path", file.getAbsolutePath().toString());// 匹配lyricsif (!file.exists()) {file = new File(parentPath + "/../" + "lyrics/" + lrcName);}Log.i("Path", file.getAbsolutePath().toString());if (!file.exists()) {stringBuffer.append(MusicManager.OperateState.READLRCFILE_FAIL);return stringBuffer.toString();}try {FileInputStream fin = new FileInputStream(file);InputStreamReader isr = new InputStreamReader(fin, "utf-8");BufferedReader br = new BufferedReader(isr);String s;boolean isLrc = false;while ((s = br.readLine()) != null) {// if(isLrc){s = s.replace("[", ""); // 去掉左边括号String lrcData[] = s.split("]");// 这句是歌词if (lrcData[0].matches("^\\d{2}:\\d{2}.\\d+$")) {int len = lrcData.length;int end = lrcData[len - 1].matches("^\\d{2}:\\d{2}.\\d+$") ? len: len - 1;for (int i = 0; i < end; i++) {LrcContent lrcContent = new LrcContent();int lrcTime = TimeUtil.getLrcMillTime(lrcData[i]);lrcContent.setLrcTime(lrcTime);if (lrcData.length == end)lrcContent.setLrcStr(""); // 空白行elselrcContent.setLrcStr(lrcData[len - 1]);lrclists.add(lrcContent);}}}// 按时间排序Collections.sort(lrclists, new LrcComparator());if (lrclists.size() == 0) {stringBuffer.append(MusicManager.OperateState.READLRC_LISTNULL);} else {stringBuffer.append(MusicManager.OperateState.READLRC_SUCCESS);}} catch (FileNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();// stringBuffer.append("未找到歌词文件");} catch (UnsupportedEncodingException e) {// TODO Auto-generated catch blocke.printStackTrace();// stringBuffer.append("不支持的编码");} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();// stringBuffer.append("IO错误");}return stringBuffer.toString();}public List<LrcContent> getLrclists() {return lrclists;}}
二,歌词解析
先摘取一段歌词ti:安静]
[ar:周杰伦]
[al:范特西]
[by:Midas]
[00:03.16]
[00:04.50]周杰伦-安静
[00:14.50]词:周杰伦 曲:周杰伦 编:钟兴民
[00:25.17]
[02:27.48][00:27.02]只剩下钢琴陪我谈了一天
[02:32.76][00:32.51]睡着的大提琴 安静的旧旧的
[02:39.60][00:38.67]
[02:40.74][00:40.48]我想你已表现的非常明白
[02:46.04][00:45.7]我懂我也知道 你没有舍不得
[02:53.23][00:52.53]
[02:54.04][00:53.76]你说你也会难过我不相信
[03:00.59][01:00.36]牵着你陪着我 也只是曾经
[03:06.63][01:06.24]希望他是真的比我还要爱你
[03:13.29][01:12.96]我才会逼自己离开
每次遍历一行,首先要把“[”替换成" ",去匹配哪些是时间部分,正则匹配“^\\d{2}:\\d{2}.\\d+$”,然后split("]"),得到一个数组data[],最后一个是内容,前面是歌词,遍历数组,装入时间歌词到list。
时间处理:转成毫秒
遍历所有行后,对list按照时间排序。
代码在上面LrcProgress。
三,LrcView控件
分为以下状态:public static String READLRC_SUCCESS="READLRC_SUCCESS";//读取本地歌词成功
public static String READLRC_LISTNULL="READLRC_LISTNULL";//读取歌词list为null
public static String READLRC_ONLINE="READLRC_ONLINE";//正在从网络加载歌词
public static String READLRCFILE_FAIL="READLRCFILE_FAIL";//读取歌词文件失败
public static String READLRCONLINE_FAIL="READLRCONLINE_FAIL";//从网络加载歌词失败
根据不同的状态绘制不同的内容。
LrcView继承自ScrollView,然后再加一层LinearLayout,歌词绘制在TextView上,按照播放时间滚动,就可以保证当前播放的歌词在屏幕中间了。
关于自定义控件,要注意对onMeasure(),onLayout(),onDraw()比较好的理解,有时候遇到onDraw()不能执行,记得加上setWillNotDraw(false),这里直接继承自ScrollView,就不需要考虑那么多了。
这里需要根据播放时间计算当前播放位置,歌词所在行,然后不同的时候,就去更新歌词界面。
调整歌词进度:
触摸监听时,ACTION_MOVE去绘制歌词进度预览(包括调整到的时间预览),ACTION_UP时调整到对应的进度。
package com.huwei.sweetmusicplayer.ui.widgets;import java.util.List; import com.huwei.sweetmusicplayer.datamanager.MusicManager;import com.huwei.sweetmusicplayer.models.LrcContent;import com.huwei.sweetmusicplayer.util.TimeUtil; import android.content.Context;import android.content.Intent;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Typeface;import android.os.Handler;import android.os.Message;import android.util.AttributeSet;import android.util.Log;import android.view.MotionEvent;import android.view.View;import android.view.ViewTreeObserver.OnScrollChangedListener;import android.widget.ScrollView;import android.widget.TextView;import android.view.View.OnTouchListener;public class LrcView extends ScrollView implements OnScrollChangedListener,OnTouchListener{private float width;//歌词视图宽度private float height;//歌词视图高度private Paint currentPaint;//当前画笔对象private Paint notCurrentPaint;//非当前画笔对象private final float textHeight=40;//文本高度private final float textSize=36;//高亮文本大小private final float notTextSize=30;//非高亮文本大小private int index;//歌词list集合下标private String lrcState;private LrcTextView lrcTextView;private List<LrcContent> lrcLists;private int scrollY;private boolean canDrawLine=false;private int pos=-1; //手指按下后歌词要到的位置private Paint linePaint;private boolean canTouchLrc=false;//是否可以触摸并调整歌词private int count=0; //绘制加载点的次数private Context mContext;public LrcView(Context context) {this(context,null);// TODO Auto-generated constructor stub}public LrcView(Context context, AttributeSet attrs) {this(context, attrs,0);// TODO Auto-generated constructor stub}public LrcView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);// TODO Auto-generated constructor stubmContext=context;this.setOnTouchListener(this); init(); }public List<LrcContent> getLrcLists() {return lrcLists;}public void setLrcLists(List<LrcContent> lrcLists) {this.lrcLists = lrcLists;//判断歌词界面是否可以触摸if(lrcLists==null||lrcLists.size()==0)canTouchLrc=false;else canTouchLrc=true;//设置index=-1this.index=-1; LayoutParams params1=new LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.MATCH_PARENT);lrcTextView=new LrcTextView(this.getContext());lrcTextView.setLayoutParams(params1); this.removeAllViews();this.addView(lrcTextView);}public int getIndex() {return index;}public void setIndex(int index) {//歌曲位置发生变化,而且手指不是调整歌词位置的状态if(this.index!=index&&pos==-1){this.scrollTo(0, (int)(index*textHeight));}this.index = index;} @Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {// TODO Auto-generated method stubsuper.onSizeChanged(w, h, oldw, oldh);this.width=w;this.height=h;}public int getIndexByLrcTime(int currentTime){for(int i=0;i<lrcLists.size();i++){if(currentTime<lrcLists.get(i).getLrcTime()){return i-1;}}return lrcLists.size()-1;}public void clear(){lrcLists=null;} public String getLrcState() {return lrcState;}public void setLrcState(String lrcState) {this.lrcState = lrcState;this.invalidate();}class LrcTextView extends TextView{public LrcTextView(Context context) {this(context,null);// TODO Auto-generated constructor stub}public LrcTextView(Context context, AttributeSet attrs) {this(context, attrs,0);// TODO Auto-generated constructor stub}public LrcTextView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);// TODO Auto-generated constructor stubthis.setWillNotDraw(false); }//绘制歌词@Overrideprotected void onDraw(Canvas canvas) {// TODO Auto-generated method stubsuper.onDraw(canvas);Log.i("LrcTextView onDraw","LrcTextView onDraw");if(canvas==null) return; int tempY=(int) height/2; if(MusicManager.OperateState.READLRC_LISTNULL.equals(lrcState)){ canvas.drawText("歌词内容为空", width/2, tempY, notCurrentPaint); return; }else if(MusicManager.OperateState.READLRCFILE_FAIL.equals(lrcState)){ canvas.drawText("未找到歌词文件", width/2, tempY, notCurrentPaint); return; } else if(MusicManager.OperateState.READLRC_SUCCESS.equals(lrcState)){ //绘制歌词 for(int i=0;i<lrcLists.size();i++,tempY+=textHeight){ if(i==index){ canvas.drawText(lrcLists.get(i).getLrcStr(), width/2, tempY, currentPaint); }else if(i==pos){ canvas.drawText(lrcLists.get(i).getLrcStr(), width/2, tempY, linePaint); }else{ canvas.drawText(lrcLists.get(i).getLrcStr(), width/2, tempY, notCurrentPaint); } } return; }else if(MusicManager.OperateState.READLRC_ONLINE.equals(lrcState)){ String drawContentStr="在线匹配歌词"; for(int i=0;i<count;i++){ drawContentStr+="."; } count++; if(count>=6) count=0; canvas.drawText(drawContentStr, width/2, tempY, notCurrentPaint); handler.sendEmptyMessageDelayed(1, 500); return; }else if(MusicManager.OperateState.READLRCONLINE_FAIL.equals(lrcState)){ canvas.drawText("从网络加载歌词失败", width/2, tempY, notCurrentPaint); return; } }@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {// TODO Auto-generated method stubsuper.onMeasure(widthMeasureSpec, heightMeasureSpec);heightMeasureSpec=(int) (height+textHeight*(lrcLists.size()-1));setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);}};@Overrideprotected void onDraw(Canvas canvas) {// TODO Auto-generated method stubsuper.onDraw(canvas);if(canDrawLine){canvas.drawLine(0, scrollY+height/2, width, scrollY+height/2, linePaint);canvas.drawText(TimeUtil.toTime(lrcLists.get(pos).getLrcTime()), 42, scrollY+height/2-2, linePaint);}}private void init(){setFocusable(true);//设置该控件可以有焦点this.setWillNotDraw(false); //高亮歌词部分currentPaint=new Paint();currentPaint.setAntiAlias(true);//设置抗锯齿currentPaint.setTextAlign(Paint.Align.CENTER);//设置文本居中//非高亮歌词部分notCurrentPaint=new Paint();notCurrentPaint.setAntiAlias(true);notCurrentPaint.setTextAlign(Paint.Align.CENTER);//linePaint=new Paint();linePaint.setAntiAlias(true);linePaint.setTextAlign(Paint.Align.CENTER);//设置画笔颜色currentPaint.setColor(Color.argb(210, 251, 248, 29)); notCurrentPaint.setColor(Color.argb(140, 255, 255, 255)); linePaint.setColor(Color.RED); currentPaint.setTextSize(textSize); currentPaint.setTypeface(Typeface.SERIF); notCurrentPaint.setTextSize(notTextSize); notCurrentPaint.setTypeface(Typeface.DEFAULT); linePaint.setTextSize(textSize); linePaint.setTypeface(Typeface.SERIF); }@Overridepublic void invalidate() {// TODO Auto-generated method stubsuper.invalidate(); lrcTextView.invalidate();}@Overridepublic void onScrollChanged() {// TODO Auto-generated method stub }@Overridepublic boolean onTouch(View v, MotionEvent event) {// TODO Auto-generated method stub//界面不能被触摸if(!canTouchLrc) return true;switch(event.getAction()){case MotionEvent.ACTION_MOVE:scrollY=this.getScrollY();pos=(int) (this.getScrollY()/textHeight);canDrawLine=true;this.invalidate();Log.i("LrcView", "ACTION_DOWN");break;caseMotionEvent.ACTION_UP:MusicManager.getInstance().setProgress(lrcLists.get(pos).getLrcTime());canDrawLine=false;pos=-1;this.invalidate();break;}return false;}private Handler handler=new Handler(){@Overridepublic void handleMessage(Message msg) {// TODO Auto-generated method stubinvalidate();}};}
下一篇智能匹配在线歌词: http://blog.csdn.net/huweigoodboy/article/details/39878063
- android音乐播放器开发 SweetMusicPlayer 智能匹配本地歌词
- android音乐播放器开发 SweetMusicPlayer 智能加载在线歌词
- android音乐播放器开发 SweetMusicPlayer 播放本地音乐
- android音乐播放器开发 SweetMusicPlayer 摇一摇换歌
- android音乐播放器开发 SweetMusicPlayer 实现思路
- android音乐播放器开发 SweetMusicPlayer 加载歌曲列表
- Android开发本地及网络Mp3音乐播放器(十五)网络音乐及歌词下载功能实现
- Android应用开发--MP3音乐播放器滚动歌词实现
- Android应用开发--MP3音乐播放器滚动歌词实现
- Android应用开发--MP3音乐播放器滚动歌词实现
- android音乐播放器开发在线加载歌词
- Android开发本地及网络Mp3音乐播放器(十六)歌词显示及滚动事件实现、ViewPager使用
- android 音乐播放器-------歌词同步 lrc
- android 音乐播放器----歌词在线下载
- Android 音乐播放器 实现歌词同步
- android 音乐播放器----歌词在线下载
- Android 音乐播放器实现歌词显示
- android 音乐播放器----歌词在线下载
- 五、结构体、文字显示与GDT/IDT初始化
- android开发学习(三)——sqlite数据库
- 六、分割源文件和中断处理
- 黑马程序员——C语言总结
- 暗时间
- android音乐播放器开发 SweetMusicPlayer 智能匹配本地歌词
- 众布仲站刨棕仲驳伎穆汲杜蛹富宋
- 芳仑纱侵列侵沃谆墒耙魄人嚼托屡
- 牧猿囱逊透兰沽粗妥运穆谂臃写粮
- HCSR04超声波传感器驱动
- iOS开发之自定义通知
- 惶叹俪毙至驳鞘铣仲菲翱杜醒蔷铣
- Java设计模式菜鸟系列(十五)建造者模式建模与实现
- 仲鞘寺仑疵尾牌疑柯旁展卵坑陈人