您的位置:首页 > 移动开发 > Android开发

Android中解析lrc歌词 同步歌曲

2015-10-08 10:58 363 查看
</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>







                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: