LRC歌词原理和实现高仿Android网易云音乐
大家好,我们是爱学啊,今天给大家带来一篇关于LRC歌词原理和在Android上如何实现歌词逐行滚动的效果,本文来自【Android开发项目实战我的云音乐】课程;逐字滚动下一篇文章讲解。
效果图
相信大家都懂一张图胜过千言万语。
效果和现在市面上大部分播放器差不多,当然如果要运用到商业项目中,肯定还需要进行一些优化,例如:滚动效果有弹性,字体大小,字体颜色等。
什么是LRC歌词
LRC是英文Lyric(歌词)的缩写,常用作逐行歌词扩展名。他是纯文本文件,格式简单,能实现歌词逐行滚动;当然目前业界大部分播放器都是在他的基础上定制了,但基本原理一样,当学完我们这篇文章后,大家也可以根据自己的需求定制。
LRC歌词格式
在实现歌词功能前,肯定需要搞明白LRC歌词格式,例如:我们找了一段LRC歌词:
[ti:爱的代价]
[ar:李宗盛]
[al:滚石香港黄金十年 李宗盛精选]
[ly:李宗盛]
[mu:李宗盛]
[ma:]
[pu:]
[by:ttpod]
[total:272073]
[offset:0]
[00:00.300]爱的代价 - 李宗盛
[00:01.979]作词:李宗盛
[00:03.312]作曲:李宗盛
[00:06.429]
[00:16.282]还记得年少时的梦吗
[00:20.575]像朵永远不调零的花
[00:24.115]陪我经过那风吹雨打
[00:27.921]看世事无常
[00:29.653]看沧桑变化
[00:32.576]那些为爱所付出的代价
[00:36.279]是永远都难忘的啊
[00:40.485]所有真心的痴心的话
可以看到内容是用换行符分割的,如果这些数据是通过接口返回,而不是直接返回一个LRC文件,那么这里面的换行符应该变为\n换行符,这一点我们也在课程中讲解到了。
每一行是一句歌词;每一行歌词又分为两部分,中括号里面是当前这行歌词的开始时间,格式为分:秒:毫秒,有些歌词可能没有毫秒,只有秒;歌词开头由于部分数据称为LRC元数据,他是用来描述这个歌词的,部分字段解释如下:
ti:title,标题,通常是歌曲名称
ar:artist,艺人名
al:album,专辑名
by:歌词创建人,这里是ttpod,指的是天天动听
total:整首歌曲时长,单位毫秒
offset:时间补偿值,单位毫秒,正值表示整体提前,负值相反
前面这些字段根据不同的播放器可能用的位置不一样,我们课程中虽然解析了这些字段,但也没有用到。
歌词滚动原理
将每行歌词前面的时间解析后,转为毫秒,这样播放器在播放的时候可以获取到播放时间,然后拿着时间查找当前时间对应哪一行歌词,然后在界面上高亮这一行歌词,或者做更多的处理,例如:字体增大等操作;就实现了歌词逐行高亮;至于滚动不同的平台不一样,滚动思路是:获取到当前时间所对应哪一行,然后我们肯定能算出每一行歌词高度,所以行*每一行高度就是滚动的高度。
歌词解析
不同的语言语法不一样,我们这里先说思路,我们的实现是Java语言。
读取该文件每一行,然后用]拆分,第二部分就是歌词,第一部分继续用:拆分,然后将三部分转为毫秒;最后将这些信息保存到对象上。
当然为了以后更好的扩展,因为歌词格式很多,可以进行一些架构:
String[] strings = content.split("\n");
lyric = new Lyric();
TreeMap<Integer, Line> lyrics = new TreeMap<>();
Map<String, Object> tags = new HashMap<>();
String lineInfo=null;
int lineNumber = 0;
for (int i = 0; i < strings.length; i++) {
try {
lineInfo=strings[i];
Line line = parserLine(tags, lineInfo);
if (line != null) {
lyrics.put(lineNumber, line);
lineNumber++;
}
} catch (Exception var9) {
var9.printStackTrace();
}
}
lyric.setLyrics(lyrics);
lyric.setTags(tags);
/**
* 解析每一行歌词
*/
private Line parserLine(Map<String, Object> tags, String lineInfo) {
if (lineInfo.startsWith("[0")) {
//歌词开始了
Line line = new Line();
int leftIndex = 1;
int rightIndex = lineInfo.length();
String[] lineComments = lineInfo.substring(leftIndex, rightIndex).split("]", -1);
//开始时间
String startTimeStr = lineComments[0];
int startTime = TimeUtil.parseInteger(startTimeStr);
line.setStartTime(startTime);
//歌词
String lineLyricsStr = lineComments[1];
line.setLineLyrics(lineLyricsStr);
return line;
}
return null;
}
歌词绘制
不同的平台也不一样,我们这里是Android,所以绘制用Canvas。我们这里的思路是:歌词View的高度是固定的,由于我们希望当前行歌词始终显示到歌词View中间,所以先算出View的中心高度,然后在该位置绘制当前行歌词,这一步根据不同的歌词处理的逻辑也不一样,但歌词可分为两类,一类是逐行,一类是逐字,对于逐行来说就直接绘制就行了,只是颜色,大小不一样而已;逐字下一节讲解;然后从当前行歌词位置像前绘制歌词,直到超出View顶部为止,在从当前行歌词向下歌词绘制,直到超出View底部为止;当前你可以使用LinearLayout添加所有歌词当前容器内,然后滚动。
private void drawLyricText(Canvas canvas) {
//在当前位置绘制正在演唱的歌词
Line line = lyricsLines.get(lineNumber);
//当前歌词的宽高
float textWidth = getTextWidth(backgroundTextPaint, line.getLineLyrics());
float textHeight = getTextHeight(backgroundTextPaint);
float centerY = (getMeasuredHeight() - textHeight) / 2 + lineNumber * getLineHeight(backgroundTextPaint) - offsetY;
float x = (getMeasuredWidth() - textWidth) / 2;
float y = centerY;
//当前歌词高亮
if (lyric.isAccurate()) {
//TODO 精确到字,歌词,下一节讲解
} else {
//精确到行
canvas.drawText(line.getLineLyrics(), x, y, foregroundTextPaint);
}
//绘制前面的歌词
for (int i = lineNumber - 1; i > 0; i--) {
//从当前行的上一行开始绘制
line = lyricsLines.get(i);
//当前歌词的宽高
textWidth = getTextWidth(backgroundTextPaint, line.getLineLyrics());
textHeight = getTextHeight(backgroundTextPaint);
x = (getMeasuredWidth() - textWidth) / 2;
y = centerY - (lineNumber - i) * getLineHeight(backgroundTextPaint);
if (y < getLineHeight(backgroundTextPaint)) {
//超出了View顶部,不再绘制
break;
}
canvas.drawText(line.getLineLyrics(), x, y, backgroundTextPaint);
}
//绘制后面的歌词
for (int i = lineNumber + 1; i < lyricsLines.size(); i++) {
//从当前行的下一行开始绘制
line = lyricsLines.get(i);
//当前歌词的宽高
textWidth = getTextWidth(backgroundTextPaint, line.getLineLyrics());
textHeight = getTextHeight(backgroundTextPaint);
x = (getMeasuredWidth() - textWidth) / 2;
y = centerY + (i - lineNumber) * getLineHeight(backgroundTextPaint);
if (y + getLineHeight(backgroundTextPaint) > getHeight()) {
//超出了View底部,不再绘制
break;
}
canvas.drawText(line.getLineLyrics(), x, y, backgroundTextPaint);
}
}
歌词滚动
Android中不同的实现方法滚动方式也不一样,如果是直接绘制,那么滚动其实就是绘制不同行歌词,给人的感觉就是滚动了;如果是将所有歌词添加到容器中,那么就可以使用容器默认的滚动;对于我们这里的实现滚动其实就是更改lineNumber值,例如;当前lineNumber为5,表示当前播放的是第5行歌词,通过用户滚动的距离就能计算出当前滚动距离是哪一行,因为我们知道每一行高度所以可以计算出当前位置,滚动到的位置,然后使用属性动画滚动:
if (valueAnimator != null && valueAnimator.isRunning()) {
valueAnimator.cancel();
}
valueAnimator = ValueAnimator.ofFloat(offsetY, distanceY);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
offsetY = (float) valueAnimator.getAnimatedValue();
invalidate();
}
});
valueAnimator.setDuration(200);
valueAnimator.setInterpolator(new DecelerateInterpolator());
valueAnimator.start();
到这里LRC歌词View核心功能基本就实现完成了,如果要深入学习可以查看我们的【Android开发项目实战我的云音乐】课程,或者在线电子书【电子书】;同时大家也可以关注我们的微信公众号【ixuea666】和Android开发交流群:702321063。
LRC歌词原理和实现高仿Android网易云音乐的更多相关文章
- 卡拉OK歌词原理和实现高仿Android网易云音乐
大家好,我们是爱学啊,继上一篇讲解了[LRC歌词原理和实现高仿Android网易云音乐],今天给大家带来一篇关于卡拉OK歌词原理和在Android上如何实现歌词逐字滚动的效果,本文来自[Android ...
- 【第二版】高仿Android网易云音乐企业级项目实战课程介绍
这是一门付费Android项目课程,我们只做付费课程:同时也感谢大家的支持. 这一节,对本课程做一个简单介绍,以及放一些项目效果图,如果想直接查看项目视频演示,可以直接在腾讯课堂查看[高仿Androi ...
- 高仿Android网易云音乐OkHttp+Retrofit+RxJava+Glide+MVC+MVVM
简介 这是一个使用Java(以后还会推出Kotlin版本)语言,从0开发一个Android平台,接近企业级的项目(我的云音乐),包含了基础内容,高级内容,项目封装,项目重构等知识:主要是使用系统功能, ...
- Swift高仿iOS网易云音乐Moya+RxSwift+Kingfisher+MVC+MVVM
效果 列文章目录 因为目录比较多,每次更新这里比较麻烦,所以推荐点击到主页,然后查看iOS Swift云音乐专栏. 目简介 这是一个使用Swift(还有OC版本)语言,从0开发一个iOS平台,接近企业 ...
- OC高仿iOS网易云音乐AFNetworking+SDWebImage+MJRefresh+MVC+MVVM
效果 因为OC版本大部分截图和Swift版本一样,所以就不再另外截图了. 列文章目录 因为目录比较多,每次更新这里比较麻烦,所以推荐点击到主页,然后查看iOS云音乐专栏. 目简介 这是一个使用OC语言 ...
- C# WPF 低仿网易云音乐(PC)歌词控件
原文:C# WPF 低仿网易云音乐(PC)歌词控件 提醒:本篇博客记录了修改的过程,废话比较多,需要项目源码和看演示效果的直接拉到文章最底部~ 网易云音乐获取歌词的api地址 http://music ...
- 网易云音乐 歌词制作软件 BesLyric (最新版本下载)
导读 BesLyric , 一款专门制作 网易云音乐 LRC 滚动歌词的软件! 搜索.下载.制作 歌词更方便! 哈哈,喜欢网易云音乐,又愁于制作歌词的童鞋有福啦!Beslyric 为你排忧解难! 本文 ...
- Android项目实战之高仿网易云音乐项目介绍
这一节我们来讲解这个项目所用到的一些技术,以及一些实现的效果图,让大家对该项目有一个整体的认识,推荐大家收藏该文章,因为我们发布文章后会在该文章里面加入链接,这样大家找着就很方便. 目录 第1章 前期 ...
- android仿网易云音乐引导页、仿书旗小说Flutter版、ViewPager切换、爆炸菜单、风扇叶片效果等源码
Android精选源码 复现网易云音乐引导页效果 高仿书旗小说 Flutter版,支持iOS.Android Android Srt和Ass字幕解析器 Material Design ViewPage ...
随机推荐
- Sql Server连表查询字段为null
这是一个坑,并且是有毒的坑. 一不小心我就掉进了这个坑里面,费了好大的力气这才从坑里面爬出来. 话不多说,开始吹BB啦. 一.简单说说遇到的问题: 连表查询,一对多. 出现 int, smallda ...
- iOS 日志获取和实时浏览器显示日志
https://juejin.im/entry/576252855bbb500063e51c7d iOS 日志获取和实时浏览器显示日志
- 1010 Radix (25 分),PTA
题目:https://pintia.cn/problem-sets/994805342720868352/problems/994805507225665536 题意:给定n1.n2两个数,求可以是两 ...
- UESTC-1963咸鱼咕咕咕(二分图匹配)
咸鱼咕咕咕 Time Limit: 1000 MS Memory Limit: 64 MB Submit Status 咸鱼有个咕咕笼. 咕咕笼可以划分成m×nm×n个格子,每个小格子可以放下 ...
- 快速掌握zabbix配置
有人说zabbix难点在配置,面对很多的配置项,不知道所以然了,其实我觉得这是没掌握好zabbix的学习方法,要掌握了zabbix的学习思路,可以在一个小时内快速掌握zabbix的各种配置,下面我将重 ...
- Spring MVC文件上传出现错误:Required MultipartFile parameter 'file' is not present
1.配置文件上传的解析器 首先需要在spring mvc的配置文件中(注意是spring mvc的配置文件而不是spring的配置文件:applicationContext.xml)配置: sprin ...
- Java 从入门到进阶之路(十三)
在之前的文章我们介绍了一下 Java 类的 private,static,final,本章我们来看一下 Java 中的抽象类和抽象方法. 我们先来看下面一段代码: // 根据周长求面积 class S ...
- VS编译命令
一.前言 由于公司要求项目需要走CI构建平台,抛弃掉之前的人工编译打包方式,所以需要调研一下项目怎么通过命令行编译出产物. 二.准备工作 在构建机器上安装vs(本文示例为vs2017) 将代码上传版本 ...
- Oracle 12C CDB、PDB常用管理命令
Oracle 12C CDB.PDB常用管理命令 --查看PDB信息(在CDB模式下) show pdbs --查看所有pdbselect name,open_mode from v$pdbs; ...
- 【spring boot】配置信息
======================================================================== 1.feign 超时配置 2.上传文件大小控制 3.J ...