Android自定义View,高仿QQ音乐歌词滚动控件!
最近在以QQ音乐为样板做一个手机音乐播放器,源码下篇博文放出。今天我想聊的是这个QQ音乐播放器中歌词显示控件的问题,和小伙伴们一起来探讨怎么实现这个歌词滚动的效果。OK,废话不多说,先来看看效果图:
好,接下来我们就来看看怎么实现这样一个效果。本文主要包括如下几方面内容:
1.歌词文件格式分析及解析
2.歌词显示控件绘制
3.关于卡拉OK模式
4.使用方式
好,那就开始吧。
1.歌词文件格式分析及解析
首先,小伙伴们需要明白歌词文件的格式都是固定的,是什么样子的呢,我们来看看下图:
我们一个歌词文件打开都是这种格式,前面 [] 中的是该行歌词显示的时间,后面一行是歌词,只有时间没有歌词的行就是伴奏时间。了解了这固定的歌词格式,剩下的就简单了,解析这段文本就行了。我创建一个LrcBean用来存放每一行的数据,这个LrcBean中包括三个属性,分别是一句歌词,该歌词开始唱的时间,该歌词唱完的时间,咦,有的小伙伴可能有疑问,唱完是什麽时候呢?就是下一句的开始时间呗。OK,那我们来看看实体类:
public class LrcBean {
private String lrc;
private long start;
private long end;
public LrcBean() {
}
public LrcBean(String text, long start, long end) {
this.lrc = text;
this.start = start;
this.end = end;
}
public String getLrc() {
return lrc;
}
public void setLrc(String lrc) {
this.lrc = lrc;
}
public long getStart() {
return start;
}
public void setStart(long start) {
this.start = start;
}
public long getEnd() {
return end;
}
public void setEnd(long end) {
this.end = end;
}
}
OK,实体类有了,接下来我们来看看实体类怎么解析歌词文本,解析过程分为两步:
1.考虑到歌词文本中可能有转义字符,我们需要先把转义字符还原
2.然后按照换行符将文本拆分,再通过字符串截取将每一行的数据提取出来。代码如下(由于转义字符显示不出来,所以我这里贴一张代码图,源码文末可以下载):
OK,通过以上方式我们就把歌词文件解析成了一个List集合,该集合中的每一项就是一句歌词,另外,在伴奏的时间段,我显示一句music。
2.歌词显示控件绘制
歌词解析完了,接下来我们就可以绘制歌词View了。绘制的整体思路是这样:
1.首先获取当前播放的时间
2.根据当前播放时间,遍历歌词的List集合,判断出当前正在播放的是List集合中的哪一句,找到该句的下标
3.遍历歌词List集合,绘制所有歌词,绘制的过程中,如果该句是正在播放的歌词,则使用高亮的画笔来绘制,否则使用普通画笔绘制。
4.判断当前是否已经换行了,如果是,则调用setScrollY方法让屏幕滚动一行。关于setScrollY方法如果小伙伴们还不太了解可以参考这篇文章View绘制详解(五),draw方法细节详解之View的滚动/滑动问题。
5.每隔100毫秒重绘View。
OK,整个流程就是这样,接下来我们来看看代码实现:
@Override
protected void onDraw(Canvas canvas) {
if (width == 0 || height == 0) {
width = getMeasuredWidth();
height = getMeasuredHeight();
}
if (list == null || list.size() == 0) {
canvas.drawText("暂无歌词", width / 2, height / 2, gPaint);
return;
} getCurrentPosition(); int currentMillis = player.getCurrentPosition();
drawLrc2(canvas);
long start = list.get(currentPosition).getStart();
float v = (currentMillis - start) > 500 ? currentPosition * 80 : lastPosition * 80 + (currentPosition - lastPosition) * 80 * ((currentMillis - start) / 500f);
setScrollY((int) v);
if (getScrollY() == currentPosition * 80) {
lastPosition = currentPosition;
}
postInvalidateDelayed(100);
} private void drawLrc2(Canvas canvas) {
for (int i = 0; i < list.size(); i++) {
if (i == currentPosition) {
canvas.drawText(list.get(i).getText(), width / 2, height / 2 + 80 * i, hPaint);
} else {
canvas.drawText(list.get(i).getText(), width / 2, height / 2 + 80 * i, gPaint);
}
}
} private void getCurrentPosition() {
try {
int currentMillis = player.getCurrentPosition();
if (currentMillis < list.get(0).getStart()) {
currentPosition = 0;
return;
}
if (currentMillis > list.get(list.size() - 1).getStart()) {
currentPosition = list.size() - 1;
return;
}
for (int i = 0; i < list.size(); i++) {
if (currentMillis >= list.get(i).getStart() && currentMillis < list.get(i).getEnd()) {
currentPosition = i;
return;
}
}
} catch (Exception e) {
// e.printStackTrace();
postInvalidateDelayed(100);
}
}
OK,这里给出一个核心代码,完整代码小伙伴们在文末可以自行下载。
3.关于卡拉OK模式
OK,经过第二个步骤之后,我们这个歌词控件已经可以根据当前播放的时间来显示高亮的歌词,同时进行歌词的滚动。有的小伙伴可能还想实现一种类似于KTV里边的那种播放效果,我们也来看一看怎么实现。还是先来说说思路吧。
1.把所有的歌词都用普通的画笔画出来
2.为当前正在播放的歌词生成一个Bitmap
3.根据当前播放时间,计算出该句歌词播放的比例,然后根据这个比例绘制第二步生成的Bitmap。
OK,根据上述的思路,我贴出核心代码如下:
for (int i = 0; i < list.size(); i++) {
canvas.drawText(list.get(i).getLrc(), width / 2, height / 2 + 80 * i, gPaint);
}
String highLineLrc = list.get(currentPosition).getLrc();
int highLineWidth = (int) gPaint.measureText(highLineLrc);
int leftOffset = (width - highLineWidth) / 2;
LrcBean lrcBean = list.get(currentPosition);
long start = lrcBean.getStart();
long end = lrcBean.getEnd();
int i = (int) ((currentMillis - start) * 1.0f / (end - start) * highLineWidth);
if (i > 0) {
Bitmap textBitmap = Bitmap.createBitmap(i, 80, Bitmap.Config.ARGB_8888);
Canvas textCanvas = new Canvas(textBitmap);
textCanvas.drawText(highLineLrc, highLineWidth / 2, 80, hPaint);
canvas.drawBitmap(textBitmap, leftOffset, height / 2 + 80 * (currentPosition - 1), null);
}
4.使用方式
OK,控件做好了,最后我们再来看看使用方式。很简单,引入这个View 的类库(文末会给出下载地址),然后传入歌词的文本,开启绘制即可,如下:
lrcView.setLrc(lrcStr);
lrcView.setPlayer(PlayUtil.player);
lrcView.init();
简单三行代码,就可以开始使用了。
项目地址https://github.com/lenve/LrcView
Android自定义View,高仿QQ音乐歌词滚动控件!的更多相关文章
- Android自定义View实现仿QQ实现运动步数效果
效果图: 1.attrs.xml中 <declare-styleable name="QQStepView"> <attr name="outerCol ...
- 动手分析安卓仿QQ联系人列表TreeView控件
因项目需要需要用到仿QQ联系人列表的控件样式,于是网上找到一个轮子(https://github.com/TealerProg/TreeView),工作完成现在简单分析一下这个源码. 一. 需要用 ...
- android自定义View之仿通讯录侧边栏滑动,实现A-Z字母检索
我们的手机通讯录一般都有这样的效果,如下图: OK,这种效果大家都见得多了,基本上所有的android手机通讯录都有这样的效果.那我们今天就来看看这个效果该怎么实现. 一.概述 1.页面功能分析 整体 ...
- Android 自定义View,仿微信视频播放按钮
闲着,尝试实现了新版微信视频播放按钮,使用的是自定义View,先来个简单的效果图...真的很简单哈. 由于暂时用不到,加上时间原因,加上实在是没意思,加上……,本控件就没有实现自定义属性,有兴趣的朋友 ...
- 我的Android进阶之旅------>Android自定义View来实现解析lrc歌词并同步滚动、上下拖动、缩放歌词的功能
前言 一LRC歌词文件简介 1什么是LRC歌词文件 2LRC歌词文件的格式 LRC歌词文件的标签类型 1标识标签 2时间标签 二解析LRC歌词 1读取出歌词文件 2解析得到的歌词内容 1表示每行歌词内 ...
- Android自定义view之仿微信录制视频按钮
本文章只写了个类似微信的录制视频的按钮,效果图如下: 一.主要的功能: 1.长按显示进度条,单击事件,录制完成回调 2.最大时间和最小时间控制 3.进度条宽度,颜色设置 二.实 ...
- Android 高仿微信支付密码输入控件
像微信支付密码控件,在app中是一个多么司空见惯的功能.最近,项目需要这个功能,于是乎就实现这个功能. 老样子,投篮需要找准角度,变成需要理清思路.对于这个"小而美"的控件,我们思 ...
- Android开源系列:仿网易Tab分类排序控件实现
前言 产品:网易新闻那个Tab排序好帅. 开发:哦~ 然后这个东东在几天后就出现了..... (PS:差不多一年没回来写博客了~~~~(>_<)~~~~,顺便把名字从 enjoy风铃 修改 ...
- Android学习之基础知识五—RecyclerView(滚动控件)
RecyclerView可以说是增强版的ListView,不仅具有ListVIew的效果,还弥补许多ListView的不足. 一.RecyclerView的基本用法 与百分比布局类似,Recycler ...
随机推荐
- Kindle Paperwhite 2使用体验
博客开通后一懒就扔下了几十天,着实自惭.鉴于是第一篇,先说点题外话. 一转眼读研的生活已经过去一年有余.曾经的同学已经在职场拼搏,同龄人的生活状态也自然地带给自己一份紧迫感:不敢再贪恋校园生活的安逸, ...
- BootStrap入门教程 (二) :BASE CSS(排版(Typography),表格(Table),表单(Forms),按钮(Buttons))
上讲回顾:Bootstrap的手脚架(Scaffolding)提供了固定(fixed)和流式(fluid)两种布局,它同时建立了一个宽达940px和12列的格网系统. 基于手脚架(Scaffoldin ...
- C++ 我想这样用(五)
上一篇说了[C with Class]语法的第一部分,下面继续第二部分: 第二部分:面向过程的编程风格 什么是面向过程我想如果你还不知道,那你绝对不是C程序员!其实我个人感觉面向过程.模块式的C编程风 ...
- Struts2零碎点整理
1. 关于 Struts2 请求的扩展名问题 1). org.apache.struts2 包下的 default.properties 中配置了 Struts2 应用的一些常量 2). struts ...
- Spring Autowiring by Type
In Spring, "Autowiring by Type" means, if data type of a bean is compatible with the data ...
- mysql查询数据库大小和表
每个mysql都有一个库information_schema,里面有一张表TABLES存储了所有数据库表的信息,因此,可以从这张表中查看数据库大小和表大小 查询数据库大小 ,),'GB') as da ...
- Unity3D之Mecanim动画系统学习笔记(八):Animator Layers(动画分层)
解决什么问题? 动画分层可以用来解决什么样的问题呢?试想一下如果你要开发一款第三人称的射击游戏,那么肯定是希望身体的动画分为上下两部分,上方根据瞄准的位置和是否射击进行动画播放,下方根据移动播放动画. ...
- MFC 应用、模板、框架、文档、视图 的关系
从该对象 如何访问其他对象 全局函数 调用全局函数AfxGetApp可以得到CWinApp应用类指针 应用 AfxGetApp()->m_pMainWnd为框架窗口指针:用CWinApp::Ge ...
- C#全角半角转换函数
Code#region 全角半角转换 /// <summary> /// 转全角的函数(SBC case) /// </summary> /// <param name= ...
- CentOS6.5安装图形界面
转载自http://www.cnblogs.com/zydev/p/5128788.html 一.使用网络安装(如果网络比较快,这个方法简单) yum groupinstall "Deskt ...