Lrc文件与音乐的同步显示

来源:互联网 发布:linux怎么加入环境变量 编辑:程序博客网 时间:2024/05/24 04:13

(第一个文档 有问题处请大家多多包含)

在Musicplayer中,如何使歌词与歌曲同步显示?

这其实很简单。

我们可以下载一个lrc歌词文件,打开该文件我们可以发现其实lrc文件中格式是很规范的
lrc文件如下:


如何使歌词与歌曲同步显示,关键点就在于lrc文件显示的格式。上图我们发现lrc歌词文件是分两部分组成  左边是歌词播放的时间,右边是歌词内容。

我们可以创建一个集合来存放该信息,该集合的存放数据类型为lrc内容的实体类且该类中属性分为歌词时间与歌词内容。同时在使用MediaPlayer类来播放音频文件时,有一个getCurrentPosition()方法获取当前播放时间。

因为通过该方法获取的时间是毫秒单位,所以我们在截取lrc文件中时间的时候先转换为毫秒,在与当前时间对比就可以知道当前时间所对应的歌词内容了。话不多说,

代码如下:

首先我们需要一个Music工具类用来存放歌词信息的实体类 与 读取歌词方法类.代码如下:

MusicUtils.java:


package com.example.test;


import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;


public class MusicUtils {


public static class MusicInfos {
private String LrcContent;
private int LrcTime;


public String getLrcContent() {
return LrcContent;
}


public void setLrcContent(String lrcContent) {
LrcContent = lrcContent;
}


public int getLrcTime() {
return LrcTime;
}


public void setLrcTime(int lrcTime) {
LrcTime = lrcTime;
}


}


public static class ReadLrc {
MusicInfos musicInfos = null;
ArrayList<MusicInfos> LrcList = null;


public ReadLrc() {
musicInfos = new MusicInfos();
LrcList = new ArrayList<MusicUtils.MusicInfos>();
}


public void Read(String file) throws IOException {
File f = new File(file);
FileInputStream fis = new FileInputStream(f);
InputStreamReader isr = new InputStreamReader(fis, "UTF-8");
BufferedReader br = new BufferedReader(isr);
String s = null;
StringBuffer sb = null;
while ((s = br.readLine()) != null) {
s = s.replace("[", "");
s = s.replace("]", "@");
String lrc_data[] = s.split("@");
if (lrc_data.length > 1) {
String lrcContent = lrc_data[1];
String lrcTime = lrc_data[0];
musicInfos.setLrcContent(lrcContent);
int time = LrcTime(lrcTime);
musicInfos.setLrcTime(time);
LrcList.add(musicInfos);
musicInfos = new MusicInfos();
}
}
br.close();
isr.close();
}


public int LrcTime(String lrcTime) {
lrcTime = lrcTime.replace(":", ".");
lrcTime = lrcTime.replace(".", "@");
String[] lrc_time = lrcTime.split("@");
String min = lrc_time[0];
String seconds = lrc_time[1];
String perseconds = lrc_time[2];
int currentTime = ((Integer.parseInt(min) * 60 + Integer
.parseInt(seconds)) * 1000 + Integer.parseInt(perseconds) * 10);
return currentTime;


}


public ArrayList<MusicInfos> getLrcList() {
return LrcList;
}
}


}


//在MusicUtils.class中我们创建两个静态内部类。在ReadLrc.class中使用Read方法来读取lrc文件。并且通过replace把lrc文件中"[" 换为空字符 把“]”换为“@”。

目的是为了通过@截取时间与歌词内容两部分 (你也可以不用替换"]"根据个人喜欢) 再把截取的内容放入 集合中。这里我们通过LrcTime()方法来把时间单位转换

为毫秒。


同时我们需要自定义视图空间来动态显示歌词内容,代码如下:

LrcView:

package com.example.test;


import java.util.ArrayList;


import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.TextView;


import com.example.test.MusicUtils.MusicInfos;


public class LrcView extends TextView {
private Paint CurrentPaint;
private ArrayList<MusicInfos> LrcContentList = new ArrayList<MusicUtils.MusicInfos>();


private Paint NotCurrentPaint;
private int index;
private float width;
private float heigth;
private float textSize = 20;
private float textHeight = 30;
private String tag = "tag";


public LrcView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);


init();
}


public LrcView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}


public LrcView(Context context) {
super(context);
init();
}


private void init() {
setFocusable(true);
CurrentPaint = new Paint();
CurrentPaint.setAntiAlias(true); // 设置是否使用抗锯齿功能,会消耗较大资源,绘制图形速度会变慢。
CurrentPaint.setTextAlign(Paint.Align.CENTER); // 设置绘制文字的对齐方向


NotCurrentPaint = new Paint();
NotCurrentPaint.setAntiAlias(true);
NotCurrentPaint.setTextAlign(Paint.Align.CENTER);


}


public void setLrcContent(ArrayList<MusicInfos> LrcContentList) {
this.LrcContentList = LrcContentList;


}


@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (canvas == null) {
return;
}
CurrentPaint.setColor(Color.YELLOW);
CurrentPaint.setTextSize(textSize);
CurrentPaint.setTypeface(Typeface.SERIF); // 设置字体(衬线)
NotCurrentPaint.setColor(Color.BLUE);
NotCurrentPaint.setTextSize(textSize);
NotCurrentPaint.setTypeface(Typeface.SERIF);


try {
canvas.drawText(LrcContentList.get(index).getLrcContent(),
width / 2, heigth / 2, CurrentPaint);


float mheight = heigth / 2;
for (int i = index - 1; i >= 0; i--) {
mheight = mheight - textHeight;
canvas.drawText(LrcContentList.get(i).getLrcContent(),
width / 2, mheight, NotCurrentPaint);
}


mheight = heigth / 2;
for (int i = index + 1; i < LrcContentList.size(); i++) {
mheight = mheight + textHeight;
canvas.drawText(LrcContentList.get(i).getLrcContent(),
width / 2, mheight, NotCurrentPaint);
}
} catch (Exception e) {
e.printStackTrace();
}


}


@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
this.heigth = h;
this.width = w;


}


public void setIndex(int Index) {
this.index = Index;
}


}

//通过setIndex()方法来从外部获取索引位置。使在onDraw()方法中通过index来画图(歌词)

最后我们需要一个MainActivity来实现这些方法。

MainActivity.class:

package com.example.test;


import java.io.IOException;
import java.util.ArrayList;


import android.app.Activity;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;


import com.example.test.MusicUtils.MusicInfos;
import com.example.test.MusicUtils.ReadLrc;


public class MainActivity extends Activity {


MediaPlayer mediaPlayer = null;
ReadLrc readLrc = null;
Button btnPlay = null;
LrcView tvLrc = null;
ArrayList<MusicInfos> Lrcinfos = new ArrayList<MusicUtils.MusicInfos>();
private int index = 0;
private int currentTime = 0;
private int countTime = 0;
private String tag = "tag";


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();


try {
readLrc.Read("sdcard/yaoyuandeta.lrc");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Lrcinfos = readLrc.getLrcList();
tvLrc.setLrcContent(Lrcinfos);
mHandler.post(runnable);
btnPlay.setOnClickListener(new OnClickListener() {


@Override
public void onClick(View v) {


play();
}
});


}


public void play() {
try {
Log.i(tag, "play1");
mediaPlayer.reset();
Log.i(tag, "play2");
mediaPlayer.setDataSource("sdcard/yaoyuandeta.mp3");
Log.i(tag, "play3");
mediaPlayer.prepare();
Log.i(tag, "play4");
mediaPlayer.start();
mediaPlayer.setLooping(true);


} catch (IllegalStateException e) {
Log.i(tag, "error1");
e.printStackTrace();
} catch (IllegalArgumentException e) {
Log.i(tag, "error2");
e.printStackTrace();
} catch (SecurityException e) {
Log.i(tag, "error3");
e.printStackTrace();
} catch (IOException e) {
Log.i(tag, "error4");
e.printStackTrace();
}
}


Handler mHandler = new Handler();
Runnable runnable = new Runnable() {


@Override
public void run() {
tvLrc.setIndex(Index());
tvLrc.invalidate();
tvLrc.postDelayed(runnable, 100);
}
};


public int Index() {
if (mediaPlayer.isPlaying()) {
currentTime = mediaPlayer.getCurrentPosition();
countTime = mediaPlayer.getDuration();
}


if (currentTime < countTime) {
for (int i = 0; i < Lrcinfos.size(); i++) {
if (i < Lrcinfos.size() - 1) {
if (currentTime > Lrcinfos.get(i).getLrcTime() && i == 0) {
index = i;
}
if (currentTime > Lrcinfos.get(i).getLrcTime()
&& currentTime <= Lrcinfos.get(i + 1).getLrcTime()) {
index = i;
}
}
if (currentTime == Lrcinfos.size() - 1
&& currentTime < Lrcinfos.get(i).getLrcTime()) {
index = i;
}
}
}
return index;
}


private void init() {


mediaPlayer = new MediaPlayer();
readLrc = new ReadLrc();
btnPlay = (Button) findViewById(R.id.btnStart);
tvLrc = (LrcView) findViewById(R.id.tv);
}


@Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
Log.i(tag, "destroy");
mediaPlayer.stop();


}


}

//这里我们不进行遍历sdcard或者通过MediaStore来获取音频文件信息,直接通过路径来获取一个示例。我们通过线程动态的向setIndex()方法中传入index值,来实时同步歌词与音乐。


xml文件如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >


    <Button
        android:id="@+id/btnStart"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="播放音乐" />


    <com.example.test.LrcView
        android:id="@+id/tv"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    </com.example.test.LrcView>


</LinearLayout>



0 0