代码已托管到码云,有兴趣的小伙伴可以下载看看
https://git.oschina.net/joy_yuan/MobilePlayer
效果图:
有一个小的遗憾,就是该MP3文件和歌词文件要在同一路径下,才能读取到歌词,否则读取不到录音文件。
将录音文件发到这里,是.lrc格式的文件,其实TXT文件的也行;如果在手机上显示是乱码的话,就改一下文件的编码为Unicode,再尝试下。
[ti:北京北京] [00:00.05]献给我最爱的老婆 --常长丽 [00:02.17]歌曲名:北京北京 [00:04.00]演唱:汪峰 [00:06.84]原立鹏 制 [00:08.62] 献给我最爱的老婆 --常长丽 [00:31.16]当我走在这里的每一条街道 [00:37.32]我的心似乎从来都不能平静 [00:45.23]就让花朵妒忌红名和电气致意 [00:51.66]我似乎听到了他这不慢的心跳 [00:59.74]我在这里欢笑我在这里哭泣 [01:06.93]我在这里活着也在这死去 [01:14.09]我在这里祈祷 我在这里迷惘 [01:21.25]我在这里寻找 在这里寻求 [04:11.76][04:04.59][02:31.70][01:27.19]北京 北京 [01:35.46]咖啡管与广场又散着天气 [01:41.73]就象夜空的哪月亮的距离 [01:49.80]人们在挣扎中相互告慰和拥抱 [01:56.14]寻找着 追著着 夜夜时的睡梦 [02:04.19]我在这欢笑 我们在这哭泣 [02:11.23]我在这活着也在这死去 [02:18.70]我在这祈祷 我在这迷惘 [02:25.76]我在这寻找 在这追求 [03:08.56]如果有一天我不得不离去 [03:14.55]我希望人们把我埋葬在这里 [03:22.95]在这忘了感觉到我在存在 [03:29.10]在这有太多有我眷恋的东西 [03:37.38]我在这欢笑 我在这哭泣 [03:44.55]我在这里活着也在这死去 [03:51.74]我在这里祈祷 我在这里迷惘 [03:58.90]我在这里寻找 也在这死去 [04:04.35]北京 北京 [04:11.48]北京 北京
1、新建一个解析歌词的类,接收一个File类型的文件,由于解析的类太复杂,就不在这里分析,直接贴源码,大家以后用的时候直接参考即可
package com.yuanlp.mobileplayer.utils;
import com.yuanlp.mobileplayer.bean.Lyrc;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
/**
* Created by 原立鹏 on 2017/7/31.
* 解析歌词工具类
*/
public class LyrcUtils {
/**
* 得到解析好的歌词列表
* @return
*/
public ArrayList<Lyrc> getLyrcs() {
return Lyrcs;
}
private ArrayList<Lyrc> Lyrcs;
/**
* 是否存在歌词
* @return
*/
public boolean isExistsLyrc() {
return isExistsLyrc;
}
/**
* 是否存在歌词
*/
private boolean isExistsLyrc = false;
/**
* 读取歌词文件
* @param file /mnt/scard/audio/beijingbeijing.txt
*/
public void readLyrcFile(File file){
if(file == null || !file.exists()){
//歌词文件不存在
Lyrcs = null;
isExistsLyrc = false;
}else{
//歌词文件存在
//1.解析歌词 一行的读取-解析
Lyrcs = new ArrayList<>();
isExistsLyrc = true;
BufferedReader reader = null;
try {
reader = new BufferedReader(new InputStreamReader(new FileInputStream(file),getCharset(file)));
String line = "";
while ((line = reader.readLine())!= null){
line = parsedLyrc(line);//
}
reader.close();
} catch (Exception e) {
e.printStackTrace();
}
//2.排序
Collections.sort(Lyrcs, new Comparator<Lyrc>() {
@Override
public int compare(Lyrc lhs, Lyrc rhs) {
if(lhs.getTimePosition() < rhs.getTimePosition()){
return -1;
}else if(lhs.getTimePosition() > rhs.getTimePosition()){
return 1;
}else{
return 0;
}
}
});
//3.计算每句高亮显示的时间
for(int i=0;i<Lyrcs.size();i++){
Lyrc oneLyrc = Lyrcs.get(i);
if(i+1 < Lyrcs.size()){
Lyrc twoLyrc = Lyrcs.get(i+1);
oneLyrc.setSleepTime(twoLyrc.getTimePosition()-oneLyrc.getTimePosition());
}
}
}
}
/**
* 判断文件编码
* @param file 文件
* @return 编码:GBK,UTF-8,UTF-16LE
*/
public 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;
}
/**
* 解析一句歌词
* @param line [02:04.12][03:37.32][00:59.73]我在这里欢笑
* @return
*/
private String parsedLyrc(String line) {
////indexOf第一次出现[的位置
int pos1 = line.indexOf("[");//0,如果没有返回-1
int pos2 = line.indexOf("]");//9,如果没有返回-1
if(pos1 ==0 && pos2 != -1){//肯定是由一句歌词
//装时间
long[] times = new long[getCountTag(line)];
String strTime =line.substring(pos1+1,pos2) ;//02:04.12
times[0] = strTime2LongTime(strTime);
String content = line;
int i = 1;
while (pos1 ==0 && pos2 != -1){
content = content.substring(pos2 + 1); //[03:37.32][00:59.73]我在这里欢笑--->[00:59.73]我在这里欢笑-->我在这里欢笑
pos1 = content.indexOf("[");//0/-1
pos2 = content.indexOf("]");//9//-1
if(pos2 != -1 ){
strTime = content.substring(pos1 + 1, pos2);//03:37.32-->00:59.73
times[i] = strTime2LongTime(strTime);
if(times[i] == -1){
return "";
}
i++;
}
}
Lyrc Lyrc = new Lyrc();
//把时间数组和文本关联起来,并且加入到集合中
for(int j = 0;j < times.length;j++){
if(times[j] !=0){//有时间戳
Lyrc.setContent(content);
Lyrc.setTimePosition(times[j]);
//添加到集合中
Lyrcs.add(Lyrc);
Lyrc = new Lyrc();
}
}
return content;//我在这里欢笑
}
return "";
}
/**
* 把String类型是时间转换成long类型
* @param strTime 02:04.12
* @return
*/
private long strTime2LongTime(String strTime) {
long result = -1;
try{
//1.把02:04.12按照:切割成02和04.12
String[] s1 = strTime.split(":");
//2.把04.12按照.切割成04和12
String[] s2 = s1[1].split("\\.");
//1.分
long min = Long.parseLong(s1[0]);
//2.秒
long second = Long.parseLong(s2[0]);
//3.毫秒
long mil = Long.parseLong(s2[1]);
result = min * 60 * 1000 + second * 1000 + mil*10;
}catch (Exception e){
e.printStackTrace();
result = -1;
}
return result;
}
/**
* 判断有多少句歌词
* @param line [02:04.12][03:37.32][00:59.73]我在这里欢笑
* @return
*/
private int getCountTag(String line) {
int result = -1;
String [] left = line.split("\\[");
String [] right = line.split("\\]");
if(left.length==0 && right.length ==0){
result = 1;
}else if(left.length > right.length){
result = left.length;
}else{
result = right.length;
}
return result;
}
}2、在audioplayerActivity里,接收到service发送的EventBus的准备好播放的事件后,获取该MP3 文件的路径,最后获取歌词并解析,然后发送到handler里来显示歌词
/**
* 订阅eventbus
*/
@Subscribe(threadMode=ThreadMode.MAIN,sticky = false,priority = 99)
public void showData(MediaItem item) {
showLyrc(); //去获取MP3文件的路径然后获取歌词文件,并解析歌词,最终显示歌词
showViewData();
checkPlayMode();
}
/**
* 去获取MP3文件的路径然后获取歌词文件,并解析歌词,最终显示歌词
*/
public void showLyrc() {
LyrcUtils lyrcUtils=new LyrcUtils();
try {
//得到录音文件的地址
String path=mservice.getAudioPath();
path=path.substring(0,path.lastIndexOf("."));
File file=new File(path+".lrc"); //先去查找lrc格式的歌词文件
if (!file.exists()){
file=new File(path+".txt"); //拼一个TXT格式的歌词文件
}
lyrcUtils.readLyrcFile(file);
showlyrcView.setLyrcs(lyrcUtils.getLyrcs());
if (lyrcUtils.isExistsLyrc()){
//存在歌词,才发消息,否则不发消息
handler.sendEmptyMessage(SHOW_LYRC);
}
} catch (RemoteException e) {
e.printStackTrace();
}
}最终实现了歌词的解析。
这样实现的歌词,匹配到不同的分辨率的手机上时,字的大小会不同,那么下面就是来解决这个显示字大小的修改,最终效果如下。
定义一个工具类,DensityUtil,里面代码如下:
/**
* 根据手机的分辨率从 dip 的单位 转成为 px(像素)
*/
public static int dip2px(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
/**
* 根据手机的分辨率从 px(像素) 的单位 转成为 dp
*/
public static int px2dip(Context context, float pxValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (pxValue / scale + 0.5f);
}那么我们只需要在各自文字设置那里,通过这个工具类,修改下文字大小即可。
在ShowlyrcView类里,把textHeight=DensityUtil.dip2px(context,20);
然后2个画笔处,修改文字大小即可
//创建画笔----当前高亮的画笔 paint=new Paint(); paint.setColor(Color.GREEN); //高亮颜色 paint.setTextSize(DensityUtil.dip2px(context,20)); paint.setAntiAlias(true); //抗锯齿 paint.setTextAlign(Paint.Align.CENTER); //对齐方式,居中显示 //白色画笔 whitepaint=new Paint(); whitepaint.setColor(Color.WHITE); whitepaint.setTextAlign(Paint.Align.CENTER); whitepaint.setTextSize(DensityUtil.dip2px(context,20)); whitepaint.setAntiAlias(true);
最终达到了将歌词匹配屏幕的大小。
本文出自 “YuanGuShi” 博客,请务必保留此出处http://cm0425.blog.51cto.com/10819451/1952517
原文:http://cm0425.blog.51cto.com/10819451/1952517