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
原创粉丝点击