大家好,欢迎来到IT知识分享网。
哎,敢接触这个东西,看了好些东西,才明白,其中,借鉴如下这位网友:http://www.cnblogs.com/wenjiang/archive/2013/05/06/3063259.html?utm_source=tuicool
但还是看得很难懂:后来终于搞明白了,特简单易懂地写下来。
首先,如果解析lrc歌词文件:有些歌词是一句接着一句按时间顺序排列好的,但是有些事重复的没有顺序排列的,如下:
[00:00.00]午夜怨曲 [00:09.00]词 \ 叶世荣. 曲 \ 黄家驹. 主唱 \ 黄家驹. [00:18.00] [00:27.00]从来不知想拥有多少的理想 [00:33.00]还离不开种种困忧 [00:40.00]勉强去掩饰失意的感觉 [00:46.00]再次听到昨日的冷嘲 [00:52.00]徘徊於街中恐怕只得孤独 [00:58.00]寻回思忆中的碎片 [01:05.00]变作了一堆草芥风中散 [01:11.00]与你奏过午夜的怨曲 [04:12.00][03:47.00][03:22.00][02:19.00][01:16.00]总有挫折打碎我的心 [04:16.00][03:51.00][03:25.00][02:22.00][01:19.00]紧抱过去抑压了的手 [04:20.00][03:55.00][03:30.00][02:27.00][01:23.00]我与你也彼此一起艰苦过 [04:00.00][03:35.00][02:32.00][01:28.00]写上每句冰冷冷的诗 [04:03.00][03:38.00][02:35.00][01:31.00]不会放弃高唱这首歌 [04:07.00][03:42.00][02:39.00][01:36.00]我与你也彼此真的相识过 [01:55.00]从回忆中找不到天真的笑声 [02:01.00]曾留不低心中斗争 [02:08.00]每次去担当失意的主角 [02:14.00]冷笑变作故事的作者 [03:11.00]啊......啊......障碍能撕破 [04:25.00] [04:27.00]BEYOND再见理想 [04:29.00]/~byfaith
但是解析也是差不多而已,只不过,找个稍微多用力一点点,
首选,建立一个map存放上面的有时间的内容;开头这些信息也没什么用,另放一个,有用则用,没用则不用;因为map存放时无序的,于是乎建立一个数组存放把key排序好的时间数组:
<span style="white-space:pre"> </span>//存放开头那些没有时间的信息 private List<String> info = new ArrayList<String>(); //存放有时间的歌词 private Map<String, String> lrcs = new Hashtable<String, String>(); //存放按照从小到大排序好的时间信息 private Object[] arr;
然后写一个主要运用indexof、substring来截取文本的方法:
//获取歌词,放进集合中 public void decodeLrc(String str) { if (str.startsWith("[ti:")) { info.add(str.substring(4, str.lastIndexOf("]"))); } else if (str.startsWith("[ar:")) { info.add(str.substring(4, str.lastIndexOf("]"))); } else if (str.startsWith("[al:")) { info.add(str.substring(4, str.lastIndexOf("]"))); } else if (str.startsWith("[la:")) { info.add(str.substring(4, str.lastIndexOf("]"))); } else if (str.startsWith("[by:")) { info.add(str.substring(4, str.lastIndexOf("]"))); } else { //这里获取歌词信息 int startIndex; int tempIndex = -1; //获取多个中括号的相同歌词的信息 while ((startIndex = str.indexOf("[", tempIndex + 1)) != -1) { int endIndex = str.indexOf("]", tempIndex + 1); String tempTime = str.substring(tempIndex + 2, endIndex); lrcs.put(tempTime, str.substring(str.lastIndexOf("]") + 1, str.length())); tempIndex = endIndex; } } }
通过上面这个方法,就把数据各自存放好了。接着写一个把hashtable的key键,也就是“时间”排序好,存放于arr数组中
<span style="white-space:pre"> </span>//把hashtable转为有秩序的组合 public void convertArrays() { arr = lrcs.keySet().toArray(); Arrays.sort(arr); }
================================================================================================================================
以上就是从lrc文件中获取歌词信息的内容。但往回看,如果读取lrc内,这样也就是一个方法:通过这个方法读取文件,然后再用上面的解析……
private void getLrcs(InputStreamReader isr) { try { BufferedReader reader = new BufferedReader(isr); String line = ""; while ((line = reader.readLine()) != null) { //一行一行读取,到方法中去解析信息 decodeLrc(line); } //key键的时间排序、组合 convertArrays(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }
===========================================================================================================
歌词信息准备好了,接着就是写一个自定义的View来显示歌词同步,这个原理大概就是这样的:假设mediaplyaer传过来的时间是X,好了,我们把上面数组中的时间转为long型,假设第一句高亮歌词是Y,和这个X作比较,如果X大于或者等于Y,那么我们就开始显示下一行的高亮(上面已经做了歌词的排序是吧),通过重绘更新UI。
整个View的代码如下:
package china.testwt; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Typeface; import android.support.annotation.Nullable; import android.util.AttributeSet; import android.view.View; import java.io.*; import java.util.*; public class MyView extends View { private int mBgCol, mCurTextCol, mNorTextCol; private int mCurrentTextSize, mNormalTextSize; //存放开头那些没有时间的信息 private List<String> info = new ArrayList<String>(); //存放有时间的歌词 private Map<String, String> lrcs = new Hashtable<String, String>(); //存放按照从小到大排序好的时间信息 private Object[] arr; //不高亮的歌词画笔 private Paint mLoseFocusPaint; //高亮的 private Paint mOnFocusePaint; //一行歌词的开始位置X private float drawTextX = 0; //Y private float drawTextY = 0; //整个View的高 private float viewHeight = 0; //间隔,移动的大小 private int mSpacing; //高亮的行数 private int mIndex = 0; //获取数据源,接口 public void setLrcSource(FileInputStream reader) { InputStreamReader isr = new InputStreamReader(reader); getLrcs(isr); } //获取当前行位置,接口 public void setLrcPostion(long position) { if (mIndex < lrcs.size()) { long tmepPos = parseTime(arr[mIndex].toString()); if (tmepPos < position) { //更新UI postInvalidate(); } } } //构造方法 public MyView(Context context) { this(context, null); } public MyView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public MyView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); //以下关联好各种自定义属性 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MyView, defStyleAttr, 0); mBgCol = a.getColor(R.styleable.MyView_bgColor, Color.GREEN); mCurTextCol = a.getColor(R.styleable.MyView_CurTextColor, Color.YELLOW); mNorTextCol = a.getColor(R.styleable.MyView_NorTextColor, Color.WHITE); mCurrentTextSize = a.getDimensionPixelSize(R.styleable.MyView_currentTextSize, 28); mNormalTextSize = a.getDimensionPixelSize(R.styleable.MyView_normalTextSize, 24); a.recycle(); mLoseFocusPaint = new Paint(); mLoseFocusPaint.setAntiAlias(true); mLoseFocusPaint.setTextSize(mNormalTextSize); mLoseFocusPaint.setColor(mNorTextCol); mLoseFocusPaint.setTypeface(Typeface.SERIF); mOnFocusePaint = new Paint(); mOnFocusePaint.setAntiAlias(true); mOnFocusePaint.setColor(mCurTextCol); mOnFocusePaint.setTextSize(mCurrentTextSize); mOnFocusePaint.setTypeface(Typeface.SANS_SERIF); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); //非wrap_content 用默认的 } //这个方法运行了才运行ondraw() @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); //从中间开始,而且歌词居中。在下面设置了 drawTextX = w * 0.5f; //高就是高了 viewHeight = h; //从0.3高度的地方开始画 drawTextY = h * 0.3f; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //话背景 canvas.drawColor(mBgCol); //居中设置 Paint p = mLoseFocusPaint; p.setTextAlign(Paint.Align.CENTER); Paint p2 = mOnFocusePaint; p2.setTextAlign(Paint.Align.CENTER); //间隔,为文字大小加上10个像素 mSpacing = mCurrentTextSize + 10; //画高亮的 canvas.drawText(lrcs.get(arr[mIndex]), drawTextX, drawTextY, p2); //画高亮上面的歌词,高度递减,透明度递减 int alphaValue = 25; float tempY = drawTextY; for (int i = mIndex - 1; i >= 0; i--) { tempY -= mSpacing; if (tempY < 0) { break; } p.setColor(Color.argb(255 - alphaValue, 255, 255, 255)); canvas.drawText(lrcs.get(arr[i]), drawTextX, tempY, p); alphaValue += 25; } //画高亮下面的歌词,高度递增,透明度递减 alphaValue = 25; tempY = drawTextY; for (int i = mIndex + 1; i < lrcs.size(); i++) { tempY += mSpacing; //超出不显示啦 if (tempY > viewHeight) { break; } p.setColor(Color.argb(255 - alphaValue, 245, 245, 245)); canvas.drawText(lrcs.get(arr[i]), drawTextX, tempY, p); //如果没超出就达到了100%透明,往后的都100%透明 if (alphaValue + 25 > 255) { alphaValue = 255; } else { alphaValue += 25; } } //准备下一行刷新,重绘,这有赖于传进来的时间对比 mIndex++; } private void getLrcs(InputStreamReader isr) { try { BufferedReader reader = new BufferedReader(isr); String line = ""; while ((line = reader.readLine()) != null) { //一行一行读取,到方法中去解析信息 decodeLrc(line); } //key键的时间排序、组合 convertArrays(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } //获取歌词,放进集合中 public void decodeLrc(String str) { if (str.startsWith("[ti:")) { info.add(str.substring(4, str.lastIndexOf("]"))); } else if (str.startsWith("[ar:")) { info.add(str.substring(4, str.lastIndexOf("]"))); } else if (str.startsWith("[al:")) { info.add(str.substring(4, str.lastIndexOf("]"))); } else if (str.startsWith("[la:")) { info.add(str.substring(4, str.lastIndexOf("]"))); } else if (str.startsWith("[by:")) { info.add(str.substring(4, str.lastIndexOf("]"))); } else { //这里获取歌词信息 int startIndex; int tempIndex = -1; //获取多个中括号的相同歌词的信息 while ((startIndex = str.indexOf("[", tempIndex + 1)) != -1) { int endIndex = str.indexOf("]", tempIndex + 1); String tempTime = str.substring(tempIndex + 2, endIndex); lrcs.put(tempTime, str.substring(str.lastIndexOf("]") + 1, str.length())); tempIndex = endIndex; } } } // 解析时间,把时间转为long @Nullable private Long parseTime(String time) { // 03:02.12 if (time.indexOf(":") != -1) { String[] min = time.split(":"); String[] sec = min[1].split("\\."); long minInt = Long.parseLong(min[0] .replaceAll("\\D+", "") .replaceAll("\r", "") .replaceAll("\n", "") .trim()); long secInt = Long.parseLong(sec[0] .replaceAll("\\D+", "") .replaceAll("\r", "") .replaceAll("\n", "") .trim()); long milInt = Long.parseLong(sec[1] .replaceAll("\\D+", "") .replaceAll("\r", "") .replaceAll("\n", "") .trim()); return minInt * 60 * 1000 + secInt * 1000 + milInt * 10; } else { return null; } } //把hashtable转为有秩序的组合 public void convertArrays() { arr = lrcs.keySet().toArray(); Arrays.sort(arr); } }
mainActivity是这样的:
package china.testwt; import android.media.MediaPlayer; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import butterknife.Bind; import butterknife.ButterKnife; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; public class MainActivity extends AppCompatActivity { private static final String path = "/storage/emulated/0/Music/Beyond - 午夜怨曲.mp3"; @Bind(R.id.songs) china.testwt.MyView songs; private MediaPlayer mPlayer; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); mPlayer = new MediaPlayer(); try { //数据源 songs.setLrcSource(new FileInputStream(new File("/storage/emulated/0/Music/Beyond - 午夜怨曲.lrc"))); } catch (FileNotFoundException e) { e.printStackTrace(); } try { mPlayer.setDataSource(path); mPlayer.setOnPreparedListener(new PreparedListener()); mPlayer.prepareAsync(); } catch (Exception e) { e.printStackTrace(); } } private class PreparedListener implements MediaPlayer.OnPreparedListener { @Override public void onPrepared(MediaPlayer mp) { mPlayer.start(); new Thread(new Runnable() { @Override public void run() { while (mPlayer.isPlaying()) { //传进去进度 songs.setLrcPostion(mPlayer.getCurrentPosition()); try { //睡觉 Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); } } }
xml是这样的:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="china.testwt.MainActivity"> <china.testwt.MyView android:id="@+id/songs" app:normalTextSize="25dp" app:currentTextSize="30dp" app:bgColor="@color/colorPrimary" android:layout_width="match_parent" android:layout_height="match_parent"/> </LinearLayout>
PS,因为上面是基于本地lrc文件写的,所以不会引起空指针。但是如果在网络情况下,就是根据歌词名去搜索网络,你懂的,有些歌词名不一定规则,搜索不到,就会引起空指针。最好在自定义文件加个判断,判断那些集合或数组非空情况下才画画
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/34096.html