1. LRC文本解读

Android中要实现滚动歌词的第一步,是需要对LRC歌词文本进行解析,首先来看一份标准的歌词文本模式:

[ti:失恋战线联盟]

[ar:草蜢]

[al:]

[00:00.00]草蜢-失恋战线联盟

[00:08.78]编辑:小婧

[01:43.33][00:16.27]她总是只留下电话号码

[01:46.97][00:19.81]从不肯让我送她回家

[01:50.61][00:23.43]听说你也曾经爱上过她

[01:54.15][00:27.07]曾经也同样无法自拔

[01:57.78][00:30.72]你说你学不会假装潇洒

[02:01.41][00:34.36]却叫我别太早放弃她

[02:05.05][00:37.99]把过去传说成一段神话

[02:08.70][00:41.59]然后笑你是一样的傻

[02:12.01][00:45.11]我们那么在乎她

[02:14.15][00:47.01]却被她全部抹杀

[02:15.96][00:48.87]越谈她越相信永远得不到回答

[02:19.57][00:52.49]到底她怎么想

[02:21.35][00:54.28]应该继续在这么

[02:23.37][00:56.36]还是说穿跑了吧

[02:26.89][00:59.80]找一个承认失恋的方法

[02:30.48][01:03.41]让心情好好地放个假

[02:34.14][01:07.00]当你我不小心又想起她

[02:45.69][02:42.20][02:37.69][01:10.60]就在记忆里画一个叉

[02:48.69]

LRC有两种类型的标签,一种是 标识标签,一种是 时间标签

标识标签,格式为[标识名:值],例如[ti:失恋战线联盟],这种预定义的标签

时间标签,格式为[mm:ss]或[mm:ss.ff](分钟:秒数.毫秒数),例如[02:48.69],其中数字必须为非负整数。

时间标签必须位于行首位置,一行歌词可以包含多个时间标签,例如[02:45.69][02:42.20][02:37.69][01:10.60]就在记忆里画一个叉,表示 “就在记忆里画一个叉” 这句在这首歌的[02:45.69][02:42.20][02:37.69][01:10.60]这四个时间节点出现过。这句歌词可以简化为

[02:45.69]就在记忆里画一个叉

[02:42.20]就在记忆里画一个叉

[02:37.69]就在记忆里画一个叉

[01:10.60]就在记忆里画一个叉

2. 从assets目录下读取LRC文件

在真实项目中一般文件需要及时去获取,此处为了方便,我们将LRC文本放在assets目录下进行读取,从assets目录下读取文件的代码如下:

/**

* 根据文件名获取assets目录下的文件内容

* @param fileName

* @return

*/

private String getLrcText(String fileName) {

String lrcText = null;

try {

InputStreamReader inputReader = new InputStreamReader(getResources().getAssets().open(fileName));

BufferedReader bufReader = new BufferedReader(inputReader);

String line = "";

while ((line = bufReader.readLine()) != null)

lrcText += line;

} catch (IOException e) {

e.printStackTrace();

}

return lrcText;

}

将text.lrc文件解析为字符串lrcText。

3. 解析由LRC文件得到的lrcText 字符串

创建LrcEntity类,用于解析每行歌词包含的内容,该类包括3个变量:time,timeLong,text。

time: 表示当前行歌词的播放时间标签,例如[01:10.60]

timeLong: 表示当前时间标签time转换为long型后的数值,例如[01:10.60]转换为long为70060毫秒(70060=01 * 60

1000 + 10 * 1000+60)

text: 表示当前歌词的内容,例如就在记忆里画一个叉

创建的LrcEntity类如下所示:

/**

* 歌词解析基类

*/

public class LrcEntity {

public String time;

public long timeLong;

public String text;

public LrcEntity(String time, String text, long timeLong) {

this.time = time;

this.text = text;

this.timeLong = timeLong;

}

public String getTime() {

return time;

}

public void setTime(String time) {

this.time = time;

}

public long getTimeLong() {

return timeLong;

}

public void setTimeLong(long timeLong) {

this.timeLong = timeLong;

}

public String getText() {

return text;

}

public void setText(String text) {

this.text = text;

}

@Override

public int compareTo(LrcEntity entity) {

if (entity == null) {

return -1;

}

return (int) (timeLong - entity.getTimeLong());

}

/**

* 解析歌词文本

* @param lrcText

* @return

*/

private static List parseLrc(String lrcText) {

if (TextUtils.isEmpty(lrcText)) {

return null;

}

List entityList = new ArrayList<>();

// 将字符串以换行符切割

String[] array = lrcText.split("\\n");

for (String line : array) {

// 循环遍历按行解析

List list = parseLine(line);

if (list != null && !list.isEmpty()) {

entityList.addAll(list);

}

}

// 使序列按大小升序排序(由小到大)

Collections.sort(entityList);

return entityList;

}

/**

* 针对每一句歌词解析,并存到LrcEntity中

* @param line

* @return

*/

private static List parseLine(String line) {

if (TextUtils.isEmpty(line)) {

return null;

}

// 去除空格

line = line.trim();

// 正则表达式,判断line中是否有[01:10.60]格式的片段

Matcher lineMatcher = Pattern.compile("((\\[\\d\\d:\\d\\d\\.\\d\\d\\])+)(.+)").matcher(line);

// 如果没有,返回null

if (!lineMatcher.matches()) {

return null;

}

// 得到时间标签

String times = lineMatcher.group(1);

// 得到文本内容

String text = lineMatcher.group(3);

List entryList = new ArrayList<>();

Matcher timeMatcher = Pattern.compile("\\[(\\d\\d):(\\d\\d)\\.(\\d\\d)\\]").matcher(times);

while (timeMatcher.find()) {

long min = Long.parseLong(timeMatcher.group(1));// 分

long sec = Long.parseLong(timeMatcher.group(2));// 秒

long mil = Long.parseLong(timeMatcher.group(3));// 毫秒

// 得到long型时间

long time = min * DateUtils.MINUTE_IN_MILLIS + sec * DateUtils.SECOND_IN_MILLIS + mil * 10;

// 最终解析得到一个list

entryList.add(new LrcEntity(times, text, time));

}

return entryList;

}

}

代码中有很详细的注释, 上述parseLine方法中用到了正则表达式,不了解的同学请自行百度,很简单。这种解析方式的弊端在于不能解析多个时间标签同时存在,当然肯定是有方法的,但是我还没学会。。。

我们来看另外一种接地气的方法:

/**

* 针对每一句歌词解析,并存到LrcEntity中

*

* @param line [02:45.69][02:42.20][02:37.69][01:10.60]就在记忆里画一个叉

* @return

*/

public List parseLine2(String line) {

List entryList = new ArrayList<>();

int pos1 = line.indexOf("[");//0

int pos2 = line.indexOf("]");//9 indexof如果找不到返回-1

if (pos1 == 0 && pos2 != -1) {

//long数组用于存放时间戳,判断含有多少个时间标签

String[] times = new String[getCount(line)];

String strTime = line.substring(pos1, pos2+1);//[02:45.69]

// 时间标签数组

times[0] = strTime;

//判断是否还有下一个

String text = line;//[02:45.69][02:42.20][02:37.69][01:10.60]就在记忆里画一个叉

int i = 1;

while (pos1 == 0 && pos2 != -1) {//判断是否有时间的显示,既歌词

text = text.substring(pos2 + 1);//[02:42.20][02:37.69][01:10.60]就在记忆里画一个叉

pos1 = text.indexOf("[");//0

pos2 = text.indexOf("]");//9

if (pos2 != -1) {

strTime = text.substring(pos1, pos2 + 1);//[02:42.20]

times[i] = strTime;//将第二个时间戳添加到数组中

if (times[i] == "") {

return entryList;

}

}

i++;

}

LrcEntity lrcEntity = new LrcEntity();

for (int j = 0; j < times.length; j++) {

if (times[j] != null) {

lrcEntity.setText(text);

lrcEntity.setTimeLong(Str2Long(times[j]));

lrcEntity.setTime(times[j]);

entryList.add(lrcEntity);//将歌词信息添加到集合中

lrcEntity = new LrcEntity();

}

}

}

return entryList;

}

//将字符串转换为long类型

private long Str2Long(String strTime) {

long showTime = -1;

try {

strTime = strTime.substring(1,strTime.length()-1);

String[] s1 = strTime.split(":");

String[] s2 = s1[1].split("\\.");

long min = Long.parseLong(s1[0]);

long second = Long.parseLong(s2[0]);

long mil = Long.parseLong(s2[1]);

showTime = min * 60 * 1000 + second * 1000 + mil * 10;

} catch (NumberFormatException e) {

e.printStackTrace();

}

return showTime;

}

/**

* 判断当前行的歌词播放几次

*

* @param line

* @return

*/

private int getCount(String line) {

String[] split = line.split("\\]");

return split.length;

}

由于上述代码都是重复的,这里只给出针对每一句歌词解析,并存到LrcEntity中的方法,主要是针对字符串进行切割,是字符串的基本操作,相信大家对这块都很熟悉。

最后得到了对歌词文本解析的集合,其中包括时间标签,内容以及时间标签转换为long后的数据,解析这里基本就说完了,关于歌词的显示和绘制,下次再说,谢谢大家。

android歌词文件夹,Android歌词文本解析相关推荐

  1. android app文件夹,android app文件目录结构

    转:https://blog.csdn.net/luoguopeng/article/details/72832567 android app目录: SDCard/Android/data/你的应用的 ...

  2. android vold文件夹,android vold

    [实例简介] android vold模块,支持多分区挂载,支持ntfs.exfat格式挂载,压缩包中的tools目录,是一些工具,就是为了支持ntfs.exfat格式的挂载,需要把这些可执行文件拷贝 ...

  3. android 获取文件夹下的所有文件

    昨天,在做工作时,需要遍历所有一个文件夹下的所有文件夹,当时自己也不知道怎么做,后来在网上搜索了一些资料,发现其实也很简单. 1.获取SD是否可以读写,如果可以,则传入文件的路径 /*读取输入的某个文 ...

  4. android 删除目录下所有文件大小,Android 删除文件夹(文件夹以及文件夹下所有的文件)、文件...

    1.Android 删除文件夹(文件夹以及文件夹下所有的文件) //删除文件夹和文件夹里面的文件 public static void deleteDirWihtFile(File dir) { if ...

  5. android 7 创建文件夹,Android 在 res/layout 文件夹 下创建一个 子文件夹实例

    Android 资源文件夹 Layout 文件夹 Layout 文件是存放Android的布局文件的资源文件夹,但是如果你想要在里面创建子文件夹,你会发现xml文件报错. 如何在Layout文件夹下方 ...

  6. android data文件夹操作

    1,看代码 public class DBTest extends Activity {SQLiteDatabase db;Button bn = null;ListView listView;@Ov ...

  7. android 根目录uri,如何在根目录下创建一个文件夹Android

    我需要在根目录下创建CAT_IMG文件夹,并在列表视图中检索它.但CAT_IMG文件夹不在根目录中创建.我在清单文件中添加了权限.请帮我在根目录下创建一个文件夹.如何在根目录下创建一个文件夹Andro ...

  8. android sdk文件位置,Android SDK文件夹位于何处?

    我通过Air for Android用Adobe Flash创建了一个.apk应用程序.现在,我想通过这款Blackberry在线打包机为黑莓App World做好准备:https://bdsc.we ...

  9. Android获取文件夹路径

    Android获取文件夹路径 应用程序在运行的过程中如果需要向手机上保存数据,一般是把数据保存在SDcard中的. 大部分应用是直接在SDCard的根目录下创建一个文件夹,然后把数据保存在该文件夹中. ...

最新文章

  1. linux nmon
  2. mysql timestamp 晚8小时_mysql插入timeStamp类型数据时间相差8小时的解决办法
  3. 2016年第二季度全球以太网交换机销量破60亿美元
  4. one-to-many many-to-one 为什么只生成了一张表呢?
  5. greenfoot推箱子怎么做_地推需要怎么准备,才能保证地推做最有效
  6. 漫步最优化十七——点对点映射
  7. Android UI系列-----ScrollView和HorizontalScrollView
  8. 使用Angular与TypeScript构建Electron应用(二)
  9. Java重构面向过程代码_代码重构那些事儿
  10. Unity显示(内嵌)网页- UniWebView的使用教程
  11. (机器人学导论--运动学)(三)DH表达法顺向运动学
  12. 虚幻引擎 4 渲染流程分析
  13. elk6.4.3安装部署指导手册
  14. 一家AI创业公司不平凡的2018年
  15. 浅谈缓存系统的三个问题
  16. C语言设计一除法器,verilog 除法器
  17. Linux下以服务的方式部署springboot项目
  18. 如何使用Pytest生成一份完美的测试报告
  19. maven的依赖 传递性 和 排除
  20. 工程总承包(EPC)项目经理主要职能

热门文章

  1. python鼠标自动化 pyautogui.click()对软件分析工具点击失败
  2. 单钩点弹珠/片经验总结
  3. [substence designer]最近做的几个PBR材质
  4. 多cpu虚拟服务器,CPU内核越多,虚拟服务器性能越强?
  5. python程序员加班多吗_一直不明白,程序员为什么要加班。
  6. 郑州大学远程教育学院计算机应用基础,[知识]2012-10-17——郑州大学远程教育学院2012计算机应用基础考试考题及答案...
  7. 皓月 赠老婆 (机器生成的)
  8. eas bos判断数据是否处于工作流中
  9. dijkstra java pre_Edsger Dijkstra经典言论 - CSDN博客
  10. office2010连接服务器响应慢,Office2010打开文件卡顿,解决办法?