android音樂播放器開發 SweetMusicPlayer 智能匹配本地歌詞,android播放器

來源:互聯網
上載者:User

android音樂播放器開發 SweetMusicPlayer 智能匹配本地歌詞,android播放器

上一篇寫了使用MediaPlayer播放音樂,http://blog.csdn.net/huweigoodboy/article/details/39861539,現在來將一下載入本地歌詞。好了,還是用那張圖。


一,從記憶卡上匹配歌詞

將會從以下路徑匹配

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酷狗音樂播放器那樣使歌詞逐字匹配音樂

酷狗的歌詞逐字匹配 是因為酷狗歌詞的製作過程 你用酷狗製作過歌詞嗎? 如果你想做成一樣的 我建議你去製作一份歌詞才能明白。。。只用LRC寫 即使加點或者空格什麼的 也一定會影響美觀 如果你根據酷狗的歌詞製作步驟能寫出相關演算法 那程式裡的演算法就好寫了。。。

 
適合哪款音樂播放器可以顯示本地LRC歌詞?

右鍵-選擇 記事本 開啟。。。不過裡面會有時間顯示。。。。
 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.