Android中解析lrc歌词 同步歌曲
来源:互联网 发布:淘宝卖家钻石 编辑:程序博客网 时间:2024/04/30 17:06
</pre><pre name="code" class="java">//歌词解析类
public class LrcParser { /** 用于向外通知歌词载入、变化的监听器 */ public interface LyricListener { /** * 歌词载入时调用 * * @param LrcInfos * 歌词文本处理后的所有歌词句子 * @param indexOfCurSentence * 正在播放的句子在句子集合中的索引号 */ public abstract void onLyricLoaded(List<LrcInfo> LrcInfos, int indexOfCurSentence); /** * 歌词变化时调用 * * @param indexOfCurSentence * 正在播放的句子在句子集合中的索引号 */ public abstract void onLrcInfoChanged(int indexOfCurSentence); } private static final String TAG = LrcParser.class.getSimpleName(); public static final int STATE_COMPLETE=1; public static final int STATE_PROCESS=2; public static final int STATE_FAIL=3; public static final int STATE_OTHER=4; /** 句子集合 */ private ArrayList<LrcInfo> mLrcInfos = new ArrayList<LrcInfo>(); private LyricListener mLyricListener = null; private boolean mHasLyric = false; /** 当前正在播放的歌词句子的在句子集合中的索引号 */ private int mIndexOfCurrentSentence = -1; /** 用于缓存的一个正则表达式对象,识别[]中的内容,不包括中括号 */ private final Pattern mBracketPattern = Pattern .compile("(?<=\\[).*?(?=\\])"); private final Pattern mTimePattern = Pattern .compile("(?<=\\[)(\\d{2}:\\d{2}\\.?\\d{0,3})(?=\\])"); private final String mEncoding = "utf-8"; private Context mContext; public List<LrcInfo> getLrcInfos() { return mLrcInfos; } public void setLyricListener(LyricListener listener) { this.mLyricListener = listener; } public LrcParser(Context context){ this.mContext=context; } public void setIndexOfCurrentSentence(int index) { mIndexOfCurrentSentence = index; } public int getIndexOfCurrentSentence() { return mIndexOfCurrentSentence; } /** * 根据歌词文件的路径,读取出歌词文本并解析 * * @param lyricPath * 歌词文件路径 * @return true表示存在歌词,false表示不存在歌词 */ public boolean loadLyric(String lyricPath,int State) { mHasLyric = false; mLrcInfos.clear(); if (lyricPath != null) { //判断状态再修改显示信息----点击歌词时重复加载歌词需要改下 File file = new File(lyricPath); if (file.exists()) { mHasLyric = true; try { FileInputStream fr = new FileInputStream(file); InputStreamReader isr = new InputStreamReader(fr, mEncoding); BufferedReader br = new BufferedReader(isr); String line = null; // 逐行分析歌词文本 while ((line = br.readLine()) != null) { // Log.i(TAG, "lyric line:" + line); parseLine(line); } // 按时间排序句子集合 Collections.sort(mLrcInfos, new Comparator<LrcInfo>() { // 内嵌,匿名的compare类 public int compare(LrcInfo object1, LrcInfo object2) { if (object1.getStartTime() > object2 .getStartTime()) { return 1; } else if (object1.getStartTime() < object2 .getStartTime()) { return -1; } else { return 0; } } }); for (int i = 0; i < mLrcInfos.size() - 1; i++) { mLrcInfos.get(i).setDuringTime( mLrcInfos.get(i + 1).getStartTime()); } mLrcInfos.get(mLrcInfos.size() - 1) .setDuringTime(Integer.MAX_VALUE); fr.close(); } catch (Exception e) { // e.printStackTrace(); mLrcInfos.add(new LrcInfo(0,mContext.getResources().getString(R.string.fragment_playSong_nolrc))); } finally { } } else { Log.i(TAG, "歌词文件不存在"); } }else{ Log.i(TAG,State+"---"); //无歌词时显示的不同状态 switch (State){ case STATE_COMPLETE: mLrcInfos.add(new LrcInfo(0, mContext.getResources().getString(R.string.fragment_playSong_nolrc))); break; case STATE_PROCESS: mLrcInfos.add(new LrcInfo(0,mContext.getResources().getString(R.string.upgrade_downloading))); break; case STATE_FAIL: mLrcInfos.add(new LrcInfo(0, mContext.getResources().getString(R.string.fragment_playSong_nolrc))); break; case STATE_OTHER: Log.i(TAG,State+"STATE_OTHER---"); mLrcInfos.add(new LrcInfo(0, mContext.getResources().getString(R.string.fragment_playSong_other))); break; } } // 如果有谁在监听,通知它歌词载入完啦,并把载入的句子集合也传递过去 if (mLyricListener != null) { mLyricListener.onLyricLoaded(mLrcInfos, mIndexOfCurrentSentence); } /* if (mHasLyric) { Log.i(TAG, "Lyric file existed.Lyric has " + mLrcInfos.size() + " Sentences"); } else { Log.i(TAG, "Lyric file does not existed"); }*/ return mHasLyric; } /** * 根据传递过来的已播放的毫秒数,计算应当对应到句子集合中的哪一句,再通知监听者播放到的位置。 * * @param millisecond * 已播放的毫秒数 */ public void notifyTime(long millisecond) { // Log.i(TAG, millisecond+""); if (mHasLyric && mLrcInfos != null && mLrcInfos.size() != 0) { int newLyricIndex = seekSentenceIndex(millisecond); if (newLyricIndex != -1 && newLyricIndex != mIndexOfCurrentSentence) {// 如果找到的歌词和现在的不是一句。 if (mLyricListener != null) { // 告诉一声,歌词已经变成另外一句啦! mLyricListener.onLrcInfoChanged(newLyricIndex); } mIndexOfCurrentSentence = newLyricIndex; } } } private int seekSentenceIndex(long millisecond) { int findStart = 0; if (mIndexOfCurrentSentence >= 0) { // 如果已经指定了歌词,则现在位置开始 findStart = mIndexOfCurrentSentence; } try { long lyricTime = mLrcInfos.get(findStart).getStartTime(); if (millisecond > lyricTime) { // 如果想要查找的时间在现在字幕的时间之后 // 如果开始位置经是最后一句了,直接返回最后一句。 if (findStart == (mLrcInfos.size() - 1)) { return findStart; } int new_index = findStart + 1; // 找到第一句开始时间大于输入时间的歌词 while (new_index < mLrcInfos.size() && mLrcInfos.get(new_index).getStartTime() <= millisecond) { ++new_index; } // 这句歌词的前一句就是我们要找的了。 return new_index - 1; } else if (millisecond < lyricTime) { // 如果想要查找的时间在现在字幕的时间之前 // 如果开始位置经是第一句了,直接返回第一句。 if (findStart == 0) return 0; int new_index = findStart - 1; // 找到开始时间小于输入时间的歌词 while (new_index > 0 && mLrcInfos.get(new_index).getStartTime() > millisecond) { --new_index; } // 就是它了。 return new_index; } else { // 不用找了 return findStart; } } catch (IndexOutOfBoundsException e) { e.printStackTrace(); return 0; } } /** 解析每行歌词文本,一行文本歌词可能对应多个时间戳 */ private void parseLine(String line) { if (line.equals("")) { return; } String content = null; int timeLength = 0; int index = 0; Matcher matcher = mTimePattern.matcher(line); int lastIndex = -1;// 最后一个时间标签的下标 int lastLength = -1;// 最后一个时间标签的长度 // 一行文本歌词可能对应多个时间戳,如“[01:02.3][01:11:22.33]在这阳光明媚的春天里” // 一行也可能包含多个句子,如“[01:02.3]在这阳光明媚的春天里[01:02:22.33]我的眼泪忍不住流淌” List<String> times = new ArrayList<String>(); // 寻找出本行所有时间戳,存入times中 while (matcher.find()) { // 匹配的是中括号里的字符串,如01:02.3,01:11:22.33 String s = matcher.group(); index = line.indexOf("[" + s + "]"); if (lastIndex != -1 && index - lastIndex > lastLength + 2) { // 如果大于上次的大小,则中间夹了别的内容在里面 // 这个时候就要分段了 content = trimBracket(line.substring( lastIndex + lastLength + 2, index)); for (String string : times) { // 将每个时间戳对应的一份句子存入句子集合 long t = parseTime(string); if (t != -1) { mLrcInfos.add(new LrcInfo(t, content)); } } times.clear(); } times.add(s); lastIndex = index; lastLength = s.length(); } // 如果列表为空,则表示本行没有分析出任何标签 if (times.isEmpty()) { return; } timeLength = lastLength + 2 + lastIndex; if (timeLength > line.length()) { content = trimBracket(line.substring(line.length())); } else { content = trimBracket(line.substring(timeLength)); } // 将每个时间戳对应的一份句子存入句子集合 for (String s : times) { long t = parseTime(s); if (t != -1) { mLrcInfos.add(new LrcInfo(t, content)); } } } /** 去除指定字符串中包含[XXX]形式的字符串 */ private String trimBracket(String content) { String s = null; String result = content; Matcher matcher = mBracketPattern.matcher(content); while (matcher.find()) { s = matcher.group(); result = result.replace("[" + s + "]", ""); } return result; } /** 将歌词的时间字符串转化成毫秒数,如果参数是00:01:23.45 */ @SuppressLint("DefaultLocale") private long parseTime(String strTime) { String beforeDot = new String("00:00:00"); String afterDot = new String("0"); // 将字符串按小数点拆分成整秒部分和小数部分。 int dotIndex = strTime.indexOf("."); if (dotIndex < 0) { beforeDot = strTime; } else if (dotIndex == 0) { afterDot = strTime.substring(1); } else { beforeDot = strTime.substring(0, dotIndex);// 00:01:23 afterDot = strTime.substring(dotIndex + 1); // 45 } long intSeconds = 0; int counter = 0; while (beforeDot.length() > 0) { int colonPos = beforeDot.indexOf(":"); try { if (colonPos > 0) {// 找到冒号了。 intSeconds *= 60; intSeconds += Integer.valueOf(beforeDot.substring(0, colonPos)); beforeDot = beforeDot.substring(colonPos + 1); } else if (colonPos < 0) {// 没找到,剩下都当一个数处理了。 intSeconds *= 60; intSeconds += Integer.valueOf(beforeDot); beforeDot = ""; } else {// 第一个就是冒号,不可能! return -1; } } catch (NumberFormatException e) { return -1; } ++counter; if (counter > 3) {// 不会超过小时,分,秒吧。 return -1; } } // intSeconds=83 String totalTime = String.format("%d.%s", intSeconds, afterDot);// totaoTimer // = // "83.45" Double doubleSeconds = Double.valueOf(totalTime); // 转成小数83.45 return (long) (doubleSeconds * 1000);// 转成毫秒8345 }}
歌词显示xml
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:keepScreenOn="true" > <ListView android:id="@+id/lyricshow" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingTop="@dimen/register_rll_margin10" android:descendantFocusability="blocksDescendants" android:divider="@color/transparent" android:gravity="center" android:scrollbars="none" /> </RelativeLayout>
ListView歌词显示的适配器LyricAdapter
public class LyricAdapter extends BaseAdapter { private static final String TAG = LyricAdapter.class.getSimpleName(); /** 歌词句子集合 */ List<LrcInfo> mLrcInfos = null; Context mContext = null; /** 当前的句子索引号 */ int mIndexOfCurrentSentence = 0; public LyricAdapter(Context context) { mContext = context; mLrcInfos = new ArrayList<LrcInfo>(); mIndexOfCurrentSentence = 0; } /** 设置歌词,由外部调用, */ public void setLyric(List<LrcInfo> lyric) { mLrcInfos.clear(); if (lyric != null) { mLrcInfos.addAll(lyric); } mIndexOfCurrentSentence = 0; } @Override public boolean isEmpty() { // 歌词为空时,让ListView显示EmptyView if (mLrcInfos == null) { return true; } else if (mLrcInfos.size() == 0) { return true; } else { return false; } } @Override public boolean isEnabled(int position) { // 禁止在列表条目上点击 return false; } @Override public int getCount() { return mLrcInfos.size(); } @Override public Object getItem(int position) { return mLrcInfos.get(position).getContentText(); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder = null; if (convertView == null) { holder = new ViewHolder(); LayoutInflater inflater = (LayoutInflater) mContext .getSystemService(Context.LAYOUT_INFLATER_SERVICE); convertView = inflater.inflate(R.layout.layout_lyricshow_text, null); holder.lyric_line = (TextView) convertView .findViewById(R.id.lyric_line_text); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } if (position >= 0 && position < mLrcInfos.size()) { holder.lyric_line.setText(mLrcInfos.get(position) .getContentText()); } if (mIndexOfCurrentSentence == position) { // 当前播放到的句子设置为橙色,字体大小更大 holder.lyric_line.setTextColor(mContext.getResources().getColor(R.color.app_main_color)); holder.lyric_line.setTextSize(TypedValue.COMPLEX_UNIT_PX,mContext.getResources().getDimensionPixelSize(R.dimen.font_size_10)); } else { // 其他的句子设置为暗色,字体大小较小 holder.lyric_line.setTextColor(mContext.getResources().getColor( R.color.search_xia)); holder.lyric_line.setTextSize(TypedValue.COMPLEX_UNIT_PX,mContext.getResources().getDimensionPixelSize(R.dimen.font_size_5)); } if(mLrcInfos.get(position).getContentText().equals(mContext.getResources().getString(R.string.fragment_playSong_nolrc))||mLrcInfos.get(position).getContentText().equals(mContext.getResources().getString(R.string.upgrade_downloading))||mLrcInfos.get(position).getContentText().equals(mContext.getResources().getString(R.string.fragment_playSong_other))){ // 当为无歌词时设置为暗色,字体大小较小 holder.lyric_line.setTextColor(mContext.getResources().getColor( R.color.wizad_bg_color)); holder.lyric_line.setTextSize(TypedValue.COMPLEX_UNIT_PX,mContext.getResources().getDimensionPixelSize(R.dimen.font_size_5)); } return convertView; } public void setCurrentSentenceIndex(int index) { mIndexOfCurrentSentence = index; } static class ViewHolder { TextView lyric_line; } public void cleanLrcInfoList(){ if(mLrcInfos!=null){ mLrcInfos.clear(); } }}在歌词显示的Activity中主要的代码
1.主要的歌词监听事件
private LrcParser.LyricListener mLyricListener = new LrcParser.LyricListener(){ @Override public void onLyricLoaded(List<LrcInfo> LrcInfos, int indexOfCurSentence) { if (LrcInfos != null) { mLyricAdapter.setLyric(LrcInfos); mLyricAdapter.setCurrentSentenceIndex(indexOfCurSentence); mLyricAdapter.notifyDataSetChanged(); } } @Override public void onLrcInfoChanged(int indexOfCurSentence) { mLyricAdapter.setCurrentSentenceIndex(indexOfCurSentence); mLyricAdapter.notifyDataSetChanged(); mLrcListView.smoothScrollToPositionFromTop(indexOfCurSentence, mLrcListView.getHeight() / 2, 500); } };
2.在进度条改变时通知歌词时间的改变
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { if(mLrcParser!=null){ mLrcParser.notifyTime(progress); } } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onStopTrackingTouch(SeekBar seekBar) { isSeekBarTouch = true; // 通过进度条快进 if (musicPlayService != null) { if (seekBar.getProgress() > seekBar.getSecondaryProgress()) { seekBar.setProgress(seekBar.getSecondaryProgress()); } musicPlayService.seekPlayer(seekBar.getProgress()); <span style="color:#ff0000;">if(mLrcParser!=null){ mLrcParser.notifyTime(seekBar.getProgress()); }</span> } else { seekBar.setProgress(0); } } });3.显示歌词在onCreate中
<span style="white-space:pre"></span> mLrcParser.setLyricListener(mLyricListener); mLrcListView = (ListView) findViewById(R.id.lyricshow); mLrcListView.setAdapter(mLyricAdapter);
<span style="white-space:pre"></span>//把歌词所在的地址传过去,和获取歌词的状态<pre name="code" class="java"> <span style="white-space:pre"></span><span style="color:#ff0000;">mLrcParser.loadLyric(filePath, LrcParser.STATE_COMPLETE);</span>
0 0
- Android中解析lrc歌词 同步歌曲
- android中解析lrc歌词
- android 音乐播放器-------歌词同步 lrc
- Android开发----lrc歌词的同步展示
- 网页中LRC歌词同步显示
- Android解析lrc里的歌词
- LRC 歌词同步
- Android自定义View来实现解析lrc歌词并同步滚动、上下拖动、缩放歌词的功能[转]
- lrc 歌词解析项目
- LRC歌词解析
- Java LRC 歌词解析
- LRC歌词解析
- js解析lrc歌词
- 歌词同步的实现(lrc)
- android Mp3播放器之Lrc歌词解析
- 正则表达式练习: android 歌词解析lrc 转 txt 代码
- 我的Android进阶之旅------>Android自定义View来实现解析lrc歌词并同步滚动、上下拖动、缩放歌词的功能
- VC++lrc歌词解析类
- 使用Java调用以WSDL形式发布的web service
- 初识 Objective - C
- 程序题目6-1
- iOS Xcode7 ‘/Users/**/Framework/SDKs/PolymerPay/Library/mobStat/lib**SDK.a(**ForSDK.o)’does not con
- hive优化方式和使用技巧
- Android中解析lrc歌词 同步歌曲
- android 电容屏(一):电容屏基本原理篇
- Teechart v8在VS中的AddArray用法
- 语义分析的一些方法(中篇)
- Java-判断页面过来的请求方式
- 将数组作为函数的参数的方法
- mantis使用案例
- Reactor 和 proactor 的区别
- JAVA设计模式之单例模式