




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
【移动应用开发技术】Android怎么自定义View实现简约风歌词控件
本篇内容介绍了“Android怎么自定义View实现简约风歌词控件”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让在下带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!首先,我们得知道正常的歌词格式是怎样的,大概是长这个样子:
1[ti:喜欢你]
2[ar:.]
3[al:]
4[by:]
5[offset:0]
6[00:00.10]喜欢你-G.E.M.邓紫棋(GemTang)
7[00:00.20]词:黄家驹
8[00:00.30]曲:黄家驹
9[00:00.40]编曲:LupoGroinig10[00:00.50]11[00:12.65]细雨带风湿透黄昏的街道12[00:18.61]抹去雨水双眼无故地仰望13[00:24.04]望向孤单的晚灯14[00:26.91]15[00:27.44]是那伤感的记忆16[00:30.52]17[00:34.12]再次泛起心里无数的思念18[00:39.28]19[00:40.10]以往片刻欢笑仍挂在脸上20[00:45.49]愿你此刻可会知21[00:48.23]22[00:48.95]是我衷心的说声23[00:53.06]24[00:54.35]喜欢你那双眼动人25[00:59.35]26[01:00.10]笑声更迷人27[01:02.37]28[01:03.15]愿再可轻抚你29[01:08.56]30[01:09.35]那可爱面容31[01:12.40]挽手说梦话32[01:14.78]33[01:15.48]像昨天你共我34[01:20.84]35[01:26.32]满带理想的我曾经多冲动36[01:32.45]屡怨与她相爱难有自由37[01:37.82]愿你此刻可会知38[01:40.40]39[01:41.25]是我衷心的说声40[01:44.81]41[01:46.39]喜欢你那双眼动人42[01:51.72]43[01:52.42]笑声更迷人44[01:54.75]45[01:55.48]愿再可轻抚你46[02:00.93]47[02:01.68]那可爱面容48[02:03.99]49[02:04.73]挽手说梦话50[02:07.13]51[02:07.82]像昨天你共我52[02:14.53]53[02:25.54]每晚夜里自我独行54[02:29.30]随处荡多冰冷55[02:35.40]56[02:37.83]以往为了自我挣扎57[02:41.62]从不知她的痛苦58[02:52.02]59[02:54.11]喜欢你那双眼动人60[03:00.13]笑声更迷人61[03:02.38]62[03:03.14]愿再可轻抚你63[03:08.77]64[03:09.33]那可爱面容65[03:11.71]66[03:12.41]挽手说梦话67[03:14.61]68[03:15.45]像昨天你共我
1[ti:喜欢你]
2[ar:.]
3[al:]
4[by:]
5[offset:0]
6[00:00.10]喜欢你-G.E.M.邓紫棋(GemTang)
7[00:00.20]词:黄家驹
8[00:00.30]曲:黄家驹
9[00:00.40]编曲:LupoGroinig10[00:00.50]11[00:12.65]细雨带风湿透黄昏的街道12[00:18.61]抹去雨水双眼无故地仰望13[00:24.04]望向孤单的晚灯14[00:26.91]15[00:27.44]是那伤感的记忆16[00:30.52]17[00:34.12]再次泛起心里无数的思念18[00:39.28]19[00:40.10]以往片刻欢笑仍挂在脸上20[00:45.49]愿你此刻可会知21[00:48.23]22[00:48.95]是我衷心的说声23[00:53.06]24[00:54.35]喜欢你那双眼动人25[00:59.35]26[01:00.10]笑声更迷人27[01:02.37]28[01:03.15]愿再可轻抚你29[01:08.56]30[01:09.35]那可爱面容31[01:12.40]挽手说梦话32[01:14.78]33[01:15.48]像昨天你共我34[01:20.84]35[01:26.32]满带理想的我曾经多冲动36[01:32.45]屡怨与她相爱难有自由37[01:37.82]愿你此刻可会知38[01:40.40]39[01:41.25]是我衷心的说声40[01:44.81]41[01:46.39]喜欢你那双眼动人42[01:51.72]43[01:52.42]笑声更迷人44[01:54.75]45[01:55.48]愿再可轻抚你46[02:00.93]47[02:01.68]那可爱面容48[02:03.99]49[02:04.73]挽手说梦话50[02:07.13]51[02:07.82]像昨天你共我52[02:14.53]53[02:25.54]每晚夜里自我独行54[02:29.30]随处荡多冰冷55[02:35.40]56[02:37.83]以往为了自我挣扎57[02:41.62]从不知她的痛苦58[02:52.02]59[02:54.11]喜欢你那双眼动人60[03:00.13]笑声更迷人61[03:02.38]62[03:03.14]愿再可轻抚你63[03:08.77]64[03:09.33]那可爱面容65[03:11.71]66[03:12.41]挽手说梦话67[03:14.61]68[03:15.45]像昨天你共我从上面可以看出这种格式前面是开始时间,从左往右一一对应分,秒,毫秒,后面就是歌词。所以我们要创建一个实体类来保存每一句的歌词信息。1.歌词实体类LrcBean
1public
class
LrcBean
{
2
private
String
lrc;//歌词
3
private
long
start;//开始时间
4
private
long
end;//结束时间
5
6
public
String
getLrc()
{
7
return
lrc;
8
}
9
10
public
void
setLrc(String
lrc)
{
11
this.lrc
=
lrc;
12
}
13
14
public
long
getStart()
{
15
return
start;
16
}
17
18
public
void
setStart(long
start)
{
19
this.start
=
start;
20
}
21
22
public
long
getEnd()
{
23
return
end;
24
}
25
26
public
void
setEnd(long
end)
{
27
this.end
=
end;
28
}
29}每句歌词,我们需要开始时间,结束时间和歌词这些信息,那么你就会有疑问了?上面提到的歌词格式好像只有歌词开始时间,那我们怎么知道结束时间呢?其实很简单,这一句歌词的开始时间就是上一句歌词的结束时间。有了歌词实体类,我们就得开始对歌词进行解析了!2.解析歌词工具类LrcUtil
1public
class
LrcUtil
{
2
3
/**
4
*
解析歌词,将字符串歌词封装成LrcBean的集合
5
*
@param
lrcStr
字符串的歌词,歌词有固定的格式,一般为
6
*
[ti:喜欢你]
7
*
[ar:.]
8
*
[al:]
9
*
[by:]
10
*
[offset:0]
11
*
[00:00.10]喜欢你
-
G.E.M.
邓紫棋
(Gem
Tang)
12
*
[00:00.20]词:黄家驹
13
*
[00:00.30]曲:黄家驹
14
*
[00:00.40]编曲:Lupo
Groinig
15
*
@return
歌词集合
16
*/
17
public
static
List<LrcBean>
parseStr2List(String
lrcStr){
18
List<LrcBean>
res
=
new
ArrayList<>();
19
//根据转行字符对字符串进行分割
20
String[]
subLrc
=
lrcStr.split("
");
21
//跳过前四行,从第五行开始,因为前四行的歌词我们并不需要
22
for
(int
i
=
5;
i
<
subLrc.length;
i++)
{
23
String
lineLrc
=
subLrc[i];
24
//[00:00.10]喜欢你
-
G.E.M.
邓紫棋
(Gem
Tang)
25
String
min
=
lineLrc.substring(lineLrc.indexOf("[")+1,lineLrc.indexOf("[")+3);
26
String
sec
=
lineLrc.substring(lineLrc.indexOf(":")+1,lineLrc.indexOf(":")+3);
27
String
mills
=
lineLrc.substring(lineLrc.indexOf(".")+1,lineLrc.indexOf(".")+3);
28
//进制转化,转化成毫秒形式的时间
29
long
startTime
=
getTime(min,sec,mills);
30
//歌词
31
String
lrcText
=
lineLrc.substring(lineLrc.indexOf("]")+1);
32
//有可能是某个时间段是没有歌词,则跳过下面
33
if(lrcText.equals(""))
continue;
34
//在第一句歌词中有可能是很长的,我们只截取一部分,即歌曲加演唱者
35
//比如
光年之外
(《太空旅客(Passengers)》电影中国区主题曲)
-
G.E.M.
邓紫棋
(Gem
Tang)
36
if
(i
==
5)
{
37
int
lineIndex
=
lrcText.indexOf("-");
38
int
first
=
lrcText.indexOf("(");
39
if(first<lineIndex&&first!=-1){
40
lrcText
=
lrcText.substring(0,first)+lrcText.substring(lineIndex);
41
}
42
LrcBean
lrcBean
=
new
LrcBean();
43
lrcBean.setStart(startTime);
44
lrcBean.setLrc(lrcText);
45
res.add(lrcBean);
46
continue;
47
}
48
//添加到歌词集合中
49
LrcBean
lrcBean
=
new
LrcBean();
50
lrcBean.setStart(startTime);
51
lrcBean.setLrc(lrcText);
52
res.add(lrcBean);
53
//如果是最后一句歌词,其结束时间是不知道的,我们将人为的设置为开始时间加上100s
54
if(i
==
subLrc.length-1){
55
res.get(res.size()-1).setEnd(startTime+100000);
56
}else
if(res.size()>1){
57
//当集合数目大于1时,这句的歌词的开始时间就是上一句歌词的结束时间
58
res.get(res.size()-2).setEnd(startTime);
59
}
60
61
}
62
return
res;
63
}
64
65
/**
66
*
根据时分秒获得总时间
67
*
@param
min
分钟
68
*
@param
sec
秒
69
*
@param
mills
毫秒
70
*
@return
总时间
71
*/
72
private
static
long
getTime(String
min,String
sec,String
mills){
73
return
Long.valueOf(min)*60*1000+Long.valueOf(sec)*1000+Long.valueOf(mills);
74
}
75}相信上面的代码和注释已经将这个歌词解析解释的挺明白了,需要注意的是上面对i=5,也就是歌词真正开始的第一句做了特殊处理,因为i=5这句有可能是很长的,假设i=5是“光年之外(《太空旅客(Passengers)》电影中国区主题曲)-G.E.M.邓紫棋(GemTang)”这句歌词,如果我们不做特殊处理,在后面绘制的时候,就会发现这句歌词会超过屏幕大小,很影响美观,所以我们只截取歌曲名和演唱者,有些说明直接省略掉了。解析好了歌词,接下来就是重头戏-歌词绘制!歌词绘制就涉及到了自定义View的知识,所以还未接触自定义View的小伙伴需要先去看看自定View的基础知识。歌词绘制的主要工作主要由下面几部分构成:为歌词控件设置自定义属性,在构造方法中获取并设置自定义属性的默认值初始化两支画笔。分别是歌词普通画笔,歌词高亮画笔。获取当前播放歌词的位置画歌词,根据当前播放歌词的位置来决定用哪支画笔画歌词随歌曲播放同步滑动重新绘制1.设置自定View属性,在代码中设置默认值在res文件中的values中新建一个attrs.xml文件,然后定义歌词的自定义View属性1<?xml
version="1.0"
encoding="utf-8"?>
2<resources>
3
<declare-styleable
name="LrcView">
4
<attr
name="highLineTextColor"
format="color|reference|integer"/>
5
<attr
name="lrcTextColor"
format="color|reference|integer"/>
6
<attr
name="lineSpacing"
format="dimension"/>
7
<attr
name="textSize"
format="dimension"/>
8
</declare-styleable>
9</resources>这里只自定义了歌词颜色,歌词高亮颜色,歌词大小,歌词行间距的属性,可根据自己需要自行添加。然后在Java代码中,设置默认值。
1
private
int
lrcTextColor;//歌词颜色
2
private
int
highLineTextColor;//当前歌词颜色
3
private
int
width,
height;//屏幕宽高
4
private
int
lineSpacing;//行间距
5
private
int
textSize;//字体大小
6
7
public
LrcView(Context
context,
@Nullable
AttributeSet
attrs,
int
defStyleAttr)
{
8
super(context,
attrs,
defStyleAttr);
9
TypedArray
ta
=
context.obtainStyledAttributes(attrs,
R.styleable.LrcView);
10
lrcTextColor
=
ta.getColor(R.styleable.LrcView_lrcTextColor,
Color.GRAY);
11
highLineTextColor
=
ta.getColor(R.styleable.LrcView_highLineTextColor,
Color.BLUE);
12
float
fontScale
=
context.getResources().getDisplayMetrics().scaledDensity;
13
float
scale
=
context.getResources().getDisplayMetrics().density;
14
//默认字体大小为16sp
15
textSize
=
ta.getDimensionPixelSize(R.styleable.LrcView_textSize,
(int)
(16
*
fontScale));
16
//默认行间距为30dp
17
lineSpacing
=
ta.getDimensionPixelSize(R.styleable.LrcView_lineSpacing,
(int)
(30
*
scale));
18
//回收
19
ta.recycle();
20
}2.初始化两支画笔
1
private
void
init()
{
2
//初始化歌词画笔
3
dPaint
=
new
Paint();
4
dPaint.setStyle(Paint.Style.FILL);//填满
5
dPaint.setAntiAlias(true);//抗锯齿
6
dPaint.setColor(lrcTextColor);//画笔颜色
7
dPaint.setTextSize(textSize);//歌词大小
8
dPaint.setTextAlign(Paint.Align.CENTER);//文字居中
9
10
//初始化当前歌词画笔
11
hPaint
=
new
Paint();
12
hPaint.setStyle(Paint.Style.FILL);
13
hPaint.setAntiAlias(true);
14
hPaint.setColor(highLineTextColor);
15
hPaint.setTextSize(textSize);
16
hPaint.setTextAlign(Paint.Align.CENTER);
17
}我们把初始化的方法放到了构造方法中,这样就可以避免在重绘时再次初始化。另外由于我们把init方法只放到了第三个构造方法中,所以在上面两个构造方法需要将super改成this,这样就能保证哪个构造方法都能执行init方法
1
public
LrcView(Context
context)
{
2
this(context,
null);
3
}
4
5
public
LrcView(Context
context,
@Nullable
AttributeSet
attrs)
{
6
this(context,
attrs,
0);
7
}
8
9
public
LrcView(Context
context,
@Nullable
AttributeSet
attrs,
int
defStyleAttr)
{
10
super(context,
attrs,
defStyleAttr);
11
TypedArray
ta
=
context.obtainStyledAttributes(attrs,
R.styleable.LrcView);
12
13
//回收
14
ta.recycle();
15
init();
16
}3.重复执行onDraw方法因为后面的步骤都是在onDraw方法中执行的,所以我们先贴出onDraw方法中的代码
1
@Override
2
protected
void
onDraw(Canvas
canvas)
{
3
super.onDraw(canvas);
4
5
getMeasuredWidthAndHeight();//得到测量后的宽高
6
getCurrentPosition();//得到当前歌词的位置
7
drawLrc(canvas);//画歌词
8
scrollLrc();//歌词滑动
9
postInvalidateDelayed(100);//延迟0.1s刷新
10
}1.获得控件的测量后的宽高1
private
int
width,
height;//屏幕宽高
2
private
void
getMeasuredWidthAndHeight(){
3
if
(width
==
0
||
height
==
0)
{
4
width
=
getMeasuredWidth();
5
height
=
getMeasuredHeight();
6
}
7
}为什么要获得控件的宽高呢?因为在下面我们需要画歌词,画歌词时需要画的位置,这时候就需要用到控件的宽高了。2.得到当前歌词的位置
1
private
List<LrcBean>
lrcBeanList;//歌词集合
2
private
int
currentPosition;//当前歌词的位置
3
private
MediaPlayer
player;//当前的播放器
4
5
6
private
void
getCurrentPosition()
{
7
int
curTime
=
player.getCurrentPosition();
8
//如果当前的时间大于10分钟,证明歌曲未播放,则当前位置应该为0
9
if
(curTime
<
lrcBeanList.get(0).getStart()||curTime>10*60*1000)
{
10
currentPosition
=
0;
11
return;
12
}
else
if
(curTime
>
lrcBeanList.get(lrcBeanList.size()
-
1).getStart())
{
13
currentPosition
=
lrcBeanList.size()
-
1;
14
return;
15
}
16
for
(int
i
=
0;
i
<
lrcBeanList.size();
i++)
{
17
if
(curTime
>=
lrcBeanList.get(i).getStart()
&&
curTime
<=
lrcBeanList.get(i).getEnd())
{
18
currentPosition
=
i;
19
}
20
}
21
}我们根据当前播放的歌曲时间来遍历歌词集合,从而判断当前播放的歌词的位置。细心的你可能会发现在currentPosition=0中有个curTime>10601000的判断,这是因为在实际使用中发现当player还未播放时,这时候得到的curTime会很大,所以才有了这个判断(因为正常的歌曲不会超过10分钟)。在这个方法我们会发现出现了歌词集合和播放器,你可能会感到困惑,这些不是还没赋值吗?困惑就对了,所以我们需要提供外部方法来给外部传给歌词控件歌词集合和播放器。
1
//将歌词集合传给到这个自定义View中
2
public
LrcView
setLrc(String
lrc)
{
3
lrcBeanList
=
LrcUtil.parseStr2List(lrc);
4
return
this;
5
}
6
7
//传递mediaPlayer给自定义View中
8
public
LrcView
setPlayer(MediaPlayer
player)
{
9
this.player
=
player;
10
return
this;
11
}外部方法中setLrc的参数必须是前面提到的标准歌词格式的字符串形式,这样我们就能利用上文的解析工具类LrcUtil中的解析方法将字符串解析成歌词集合。3.画歌词1
private
void
drawLrc(Canvas
canvas)
{
2
for
(int
i
=
0;
i
<
lrcBeanList.size();
i++)
{
3
if
(currentPosition
==
i)
{//如果是当前的歌词就用高亮的画笔画
4
canvas.drawText(lrcBeanList.get(i).getLrc(),
width
/
2,
height
/
2
+
i
*
lineSpacing,
hPaint);
5
}
else
{
6
canvas.drawText(lrcBeanList.get(i).getLrc(),
width
/
2,
height
/
2
+
i
*
lineSpacing,
dPaint);
7
}
8
}
9
}知道了当前歌词的位置就很容易画歌词了。遍历歌词集合,如果是当前歌词,则用高亮的画笔画,其它歌词就用普通画笔画。这里需注意的是两支画笔画的位置公式都是一样的,坐标位置为x=宽的一半,y=高的一半+当前位置*行间距。随着当前位置的变化,就能画出上下句歌词来。所以其实绘制出来后你会发现歌词是从控件的正中央开始绘制的,这是为了方便与下面歌词同步滑动功能配合。4.歌词同步滑动
1
//歌词滑动
2
private
void
scrollLrc()
{
3
//下一句歌词的开始时间
4
long
startTime
=
lrcBeanList.get(currentPosition).getStart();
5
long
currentTime
=
player.getCurrentPosition();
6
7
//判断是否换行,在0.5内完成滑动,即实现弹性滑动
8
float
y
=
(currentTime
-
startTime)
>
500
?
currentPosition
*
lineSpacing
:
lastPosition
*
lineSpacing
+
(currentPosition
-
lastPosition)
*
lineSpacing
*
((currentTime
-
startTime)
/
500f);
9
scrollTo(0,(int)y);
10
if
(getScrollY()
==
currentPosition
*
lineSpacing)
{
11
lastPosition
=
currentPosition;
12
}
13
}如果不实现弹性滑动的话,只要判断当前播放歌曲的时间是否大于当前位置歌词的结束时间,然后进行scrollTo(0,(int)currentPosition*lineSpacing)滑动即可。但是为了实现弹性滑动,我们需要将一次滑动
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年江海职业技术学院单招职业技能测试题库汇编
- 系统解剖学复习测试卷
- 社交媒体在网络推广中的应用与策略
- 2025年湖南外国语职业学院单招职业倾向性测试题库新版
- 针灸考试复习试题及答案
- 2025年淮南联合大学单招职业倾向性测试题库完美版
- SMIC ERP BPR项目软件版本升级正式变更 标准程序变更 1-3-030-某大型企业SAP ERP方案48
- 2025年广东省揭阳市单招职业适应性测试题库及答案一套
- 科技型企业知识产全价值评估方法研究
- 文化的民族性与多样性+同步练习 高中政治统编版必修四哲学与文化
- 北京市丰台区2024-2025学年高三上学期期末英语试题
- 2025上海市嘉定工业区农村青年干部招聘22人历年高频重点提升(共500题)附带答案详解
- 《兽医基础》练习题及参考答案
- 2025年煤矿探放水证考试题库
- 农业机械设备运输及调试方案
- 污水处理设备的故障处理指南考核试卷
- ps 课件教学课件
- 神经外科患者早期康复护理
- 2025届浙江省宁波市镇海区镇海中学高二物理第一学期期末考试试题含解析
- 口腔颌面部发育(口腔组织病理学课件)
- 机房设备搬迁及系统割接施工方案
评论
0/150
提交评论