• android音乐播放器开发 SweetMusicPlayer 智能匹配本地歌词


    上一篇写了使用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/Lyrics
    		if (!file.exists()) {
    			file = new File(OnlineLrcUtil.getInstance().getLrcPath(
    					song.getTitle(), song.getArtist()));
    		}
    		Log.i("Path", file.getAbsolutePath().toString());
    
    		// 匹配Lyrics
    		if (!file.exists()) {
    			file = new File(parentPath + "/../" + "Lyrics/" + lrcName);
    		}
    		Log.i("Path", file.getAbsolutePath().toString());
    
    		// 匹配lyric
    		if (!file.exists()) {
    			file = new File(parentPath + "/../" + "lyric/" + lrcName);
    		}
    		Log.i("Path", file.getAbsolutePath().toString());
    
    		// 匹配Lyric
    		if (!file.exists()) {
    			file = new File(parentPath + "/../" + "Lyric/" + lrcName);
    		}
    
    		Log.i("Path", file.getAbsolutePath().toString());
    
    		// 匹配lyrics
    		if (!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(""); // 空白行
    						else
    							lrcContent.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 block
    			e.printStackTrace();
    			// stringBuffer.append("未找到歌词文件");
    		} catch (UnsupportedEncodingException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    			// stringBuffer.append("不支持的编码");
    		} catch (IOException e) {
    			// TODO Auto-generated catch block
    			e.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 stub
    		
    		mContext=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=-1
    		this.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;
    		
    		
    	}
    
     
    	@Override
    	protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    		// TODO Auto-generated method stub
    		super.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 stub
    			this.setWillNotDraw(false); 
    		}
    		
    		//绘制歌词
    		@Override
    		protected void onDraw(Canvas canvas) {
    			// TODO Auto-generated method stub
    			super.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;
    		    }
    		  
     
    		    
     
    
    		}
    
    		@Override
    		protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    			// TODO Auto-generated method stub
    			super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    			
    			
    			heightMeasureSpec=(int) (height+textHeight*(lrcLists.size()-1));
    			setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
    		}
    		
    		
    	};
    	
    	
    	
    	@Override
    	protected void onDraw(Canvas canvas) {
    		// TODO Auto-generated method stub
    		super.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);
    	    
    	}
    
    	@Override
    	public void invalidate() {
    		// TODO Auto-generated method stub
    		super.invalidate();
    		
     		lrcTextView.invalidate();
    	}
    
    	@Override
    	public void onScrollChanged() {
    		// TODO Auto-generated method stub
    	 
    		
    		
    
    	}
    
    	@Override
    	public 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;
    			case	MotionEvent.ACTION_UP:
    				MusicManager.getInstance().setProgress(lrcLists.get(pos).getLrcTime());
    				
    				canDrawLine=false;
    				pos=-1;
    				this.invalidate();
    				break;
    		}
    		
    		return false;
    	}
    	
    	private Handler handler=new Handler(){
    
    		@Override
    		public void handleMessage(Message msg) {
    			// TODO Auto-generated method stub
    			
    			
    			invalidate();
    		}
    		
    	};
    }	
    


    下一篇智能匹配在线歌词: http://blog.csdn.net/huweigoodboy/article/details/39878063


  • 相关阅读:
    今天写一篇随想,也当是回顾过去,展望未来吧。
    推荐 Word、EXCEL必备工具箱
    elasticsearch Routing 路由详解
    Python学习之字典
    ES 分片和副本数 调整及数据写入、重建索引调优
    fastJson JSON.parseObject()丢失字符串原本顺序
    Python 列表(详)
    pycharm常用快捷键
    Python学习笔记二(列表)
    python学习笔记二
  • 原文地址:https://www.cnblogs.com/wzjhoutai/p/6896300.html
Copyright © 2020-2023  润新知