演示
<uses-permission android:name
=
"android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
自定义View
/**
* 歌词播放的自定义控件
*/
public class LrcView extends TextView {
private String filePath = "/a.lrc";//文件路径
private List<Lyrc> lyrcList;//歌词内容
private int current = 0;//当前行
private int lineSpacing = 70; //行间距
//当前正在播放的行
private Paint currentPaint;
private int currentColor = Color.BLUE;//颜色
private int currentSize = 55;//字体大小
private Typeface currentTypeface = Typeface.DEFAULT_BOLD;//字体,默认的字体+粗体
//其他行
private Paint ortherPaint;
private int ortherColor = Color.GREEN;
private int ortherSize = 45;
private Typeface ortherTypeface = Typeface.SERIF;//一种字体类型
private Handler handler = new Handler() {
public void handleMessage(Message msg) {
invalidate();
}
};
public LrcView(Context context) {
this(context, null);
}
public LrcView(Context context, AttributeSet attrs) {
super(context, attrs);
lyrcList = LyrcUtil.readLRC(new File(Environment.getExternalStorageDirectory().getPath() + filePath));
currentPaint = new Paint();
currentPaint.setColor(currentColor);
currentPaint.setTextSize(currentSize);
currentPaint.setTextAlign(Align.CENTER);
currentPaint.setTypeface(currentTypeface);
ortherPaint = new Paint();
ortherPaint.setColor(ortherColor);
ortherPaint.setTextSize(ortherSize);
ortherPaint.setTextAlign(Align.CENTER);
ortherPaint.setTypeface(ortherTypeface);
}
public void setLrcType(String filePath, int lineSpacing, int currentColor, int currentSize, int ortherColor, int ortherSize) {
lyrcList = LyrcUtil.readLRC(new File(filePath));
this.lineSpacing = lineSpacing;
currentPaint.setColor(currentColor);
currentPaint.setTextSize(currentSize);
ortherPaint.setColor(ortherColor);
ortherPaint.setTextSize(ortherSize);
}
@Override
protected void onDraw(Canvas canvas) {
if (current < lyrcList.size()) {
if (lyrcList != null && lyrcList.size() > 0) {
Lyrc lyrc = null;
//画前面的内容
for (int i = current - 1; i >= 0; i--) {
lyrc = lyrcList.get(i);
canvas.drawText(lyrc.lrcString, getWidth() / 2, getHeight() / 2 + lineSpacing * (i - current), ortherPaint);
}
//画当前的内容
lyrc = lyrcList.get(current);
canvas.drawText(lyrc.lrcString, getWidth() / 2, getHeight() / 2, currentPaint);
//画后面的内容
for (int i = current + 1; i < lyrcList.size(); i++) {
lyrc = lyrcList.get(i);
canvas.drawText(lyrc.lrcString, getWidth() / 2, getHeight() / 2 + lineSpacing * (i - current), ortherPaint);
}
//告诉handler当前行的时间
lyrc = lyrcList.get(current);
handler.sendEmptyMessageDelayed(10, lyrc.sleepTime);
//当前行+1
current++;
} else canvas.drawText("未找到歌词", getWidth() / 2, getHeight() / 2, currentPaint);
}
super.onDraw(canvas);
}
}
工具类
public class LyrcUtil {
private static List<Lyrc> lyrcList;
/**
* 读取文件
*/
public static List<Lyrc> readLRC(File f) {
try {
if (f == null || !f.exists()) {
lyrcList = null;
} else {
lyrcList = new Vector<Lyrc>();
InputStream is = new BufferedInputStream(new FileInputStream(f));
BufferedReader br = new BufferedReader(new InputStreamReader(is, getCharset(f)));
String strTemp = "";
while ((strTemp = br.readLine()) != null) {
strTemp = analyzeLRC(strTemp);
}
br.close();
is.close();
// 对歌词进行排序
Collections.sort(lyrcList, new Sort());
// 计算每行歌词的停留时间
for (int i = 0; i < lyrcList.size(); i++) {
Lyrc one = lyrcList.get(i);
if (i + 1 < lyrcList.size()) {
Lyrc two = lyrcList.get(i + 1);
one.sleepTime = two.timePoint - one.timePoint;
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return lyrcList;
}
/**
* 处理一行内容
*/
private static String analyzeLRC(String text) {
try {
int pos1 = text.indexOf("[");
int pos2 = text.indexOf("]");
if (pos1 >= 0 && pos2 != -1) {
Long time[] = new Long[getPossiblyTagCount(text)];
time[0] = timeToLong(text.substring(pos1 + 1, pos2));
if (time[0] == -1) return "";
String strLineRemaining = text;
int i = 1;
while (pos1 >= 0 && pos2 != -1) {
strLineRemaining = strLineRemaining.substring(pos2 + 1);
pos1 = strLineRemaining.indexOf("[");
pos2 = strLineRemaining.indexOf("]");
if (pos2 != -1) {
time[i] = timeToLong(strLineRemaining.substring(pos1 + 1, pos2));
if (time[i] == -1) return ""; // LRCText
i++;
}
}
Lyrc tl = null;
for (int j = 0; j < time.length; j++) {
if (time[j] != null) {
tl = new Lyrc();
tl.timePoint = time[j].intValue();
tl.lrcString = strLineRemaining;
lyrcList.add(tl);
}
}
return strLineRemaining;
} else return "";
} catch (Exception e) {
return "";
}
}
private static int getPossiblyTagCount(String Line) {
String strCount1[] = Line.split("\\[");
String strCount2[] = Line.split("\\]");
if (strCount1.length == 0 && strCount2.length == 0) return 1;
else if (strCount1.length > strCount2.length) return strCount1.length;
else return strCount2.length;
}
/**
* 时间转换
*/
public static long timeToLong(String Time) {
try {
String[] s1 = Time.split(":");
int min = Integer.parseInt(s1[0]);
String[] s2 = s1[1].split("\\.");
int sec = Integer.parseInt(s2[0]);
int mill = 0;
if (s2.length > 1) mill = Integer.parseInt(s2[1]);
return min * 60 * 1000 + sec * 1000 + mill * 10;
} catch (Exception e) {
return -1;
}
}
/**
* 判断文件编码
*/
public static String getCharset(File file) {
String charset = "GBK";
byte[] first3Bytes = new byte[3];
try {
boolean checked = false;
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
bis.mark(0);
int read = bis.read(first3Bytes, 0, 3);
if (read == -1) return charset;
if (first3Bytes[0] == (byte) 0xFF && first3Bytes[1] == (byte) 0xFE) {
charset = "UTF-16LE";
checked = true;
} else if (first3Bytes[0] == (byte) 0xFE && first3Bytes[1] == (byte) 0xFF) {
charset = "UTF-16BE";
checked = true;
} else if (first3Bytes[0] == (byte) 0xEF && first3Bytes[1] == (byte) 0xBB && first3Bytes[2] == (byte) 0xBF) {
charset = "UTF-8";
checked = true;
}
bis.reset();
if (!checked) {
int loc = 0;
while ((read = bis.read()) != -1) {
loc++;
if (read >= 0xF0) break;
if (0x80 <= read && read <= 0xBF) break;
if (0xC0 <= read && read <= 0xDF) {
read = bis.read();
if (0x80 <= read && read <= 0xBF) continue;
else break;
} else if (0xE0 <= read && read <= 0xEF) {
read = bis.read();
if (0x80 <= read && read <= 0xBF) {
read = bis.read();
if (0x80 <= read && read <= 0xBF) {
charset = "UTF-8";
break;
} else break;
} else break;
}
}
}
bis.close();
} catch (Exception e) {
e.printStackTrace();
}
return charset;
}
private static class Sort implements Comparator<Lyrc> {
public Sort() {
}
public int compare(Lyrc tl1, Lyrc tl2) {
return sortUp(tl1, tl2);
}
private int sortUp(Lyrc tl1, Lyrc tl2) {
if (tl1.timePoint < tl2.timePoint) return -1;
else if (tl1.timePoint > tl2.timePoint) return 1;
else return 0;
}
}
}
bean
public class Lyrc {
public String lrcString;
public int sleepTime;
public int timePoint;
}
a.lrc
[00:00] 护花使者 [00:03] 李克勤 [00:38][00:05] 这晚在街中偶遇心中的她 [00:41][00:09] 两脚决定不听叫唤跟她归家 [00:44][00:12] 深宵的冷风 不准吹去她 [00:47][00:15] 她那幽幽眼神快要对我说话 [00:50][00:18] 纤纤身影飘飘身影默默转来吧 [00:54][00:21] 对我说浪漫情人爱我吗 [00:57][00:24] 贪心的晚风 竟敢拥吻她 [01:00][00:27] 将她秀发温温柔柔每缕每缕放下 [01:03][00:30] 卑污的晚风 不应抚慰她 [01:06][00:33] 我已决意一生护着心中的她
使用
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
LrcView lrcView = new LrcView(this);
lrcView.setLrcType(Environment.getExternalStorageDirectory().getPath() +"/a.lrc", dp2px(25), 0xffff00ff, dp2px(20), 0xff0000ff, dp2px(16));
setContentView(lrcView);
}
/**
* 根据手机的分辨率从 dp 的单位 转成为 px(像素)
*/
public int dp2px(float dpValue) {
float scale = getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
}