Android实现歌词滑动显示

继承TextView自定义LyricView

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
import java.util.List;  

import com.anjoyo.musicplayer.bean.LyricBean;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Shader;
import android.graphics.Typeface;
import android.graphics.Paint.Align;
import android.graphics.Shader.TileMode;
import android.util.AttributeSet;
import android.widget.TextView;

public class LyricView extends TextView {

/** 控件的高 */
private float mHeight;
/** 控件的宽 */
private float mWidth;
private Paint mPaint;
private Paint prePaint;
private Paint nextPaint;

/** 当前歌曲的歌词 */
private List<LyricBean> lyricBeanList;
/** 歌词进度,List中的位置 */
private int index;

private float heightSign;
private boolean start;

private Shader inShader = new LinearGradient(0, 0, 0, LYRIC_ONE_HEIGHT,
Color.argb(0xcc, 0xcc, 0xcc, 0xcc),
Color.argb(0xff, 0xff, 0xff, 0xff), TileMode.MIRROR);
private Shader outShader = new LinearGradient(0, LYRIC_ONE_HEIGHT, 0, 0,
Color.argb(0xcc, 0xcc, 0xcc, 0xcc),
Color.argb(0xff, 0xff, 0xff, 0xff), TileMode.MIRROR);

/** 歌词显示的字体大小 */
private static final int LYRIC_TEXT_SIZE = 36;
/** 每行歌词的高度 */
private static final int LYRIC_ONE_HEIGHT = 60;

public LyricView(Context context) {
super(context);
initView();
}

public LyricView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initView();
}

public LyricView(Context context, AttributeSet attrs) {
super(context, attrs);
initView();

new UpdateThread().start();
}

private void initView() {
setFocusable(true);

mPaint = new Paint();
mPaint.setAntiAlias(true);// 设置画笔的锯齿效果
mPaint.setTextAlign(Align.CENTER);
mPaint.setTextSize(LYRIC_TEXT_SIZE);
mPaint.setTypeface(Typeface.SERIF);

prePaint = new Paint(mPaint);
prePaint.setShader(outShader);
nextPaint = new Paint(mPaint);
nextPaint.setShader(inShader);
}

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (canvas == null) {
return;
}
mPaint.setColor(Color.argb(0xff, 0xff, 0xff, 0xff));//当前歌词词句颜色
if (lyricBeanList == null || lyricBeanList.size() < 1 || index > lyricBeanList.size() - 1) {
canvas.drawText("好音质,MusicPlayer!", mWidth / 2, mHeight / 2, mPaint);
return;
}
canvas.drawText(lyricBeanList.get(index).getLyric(), mWidth / 2, heightSign, mPaint);

mPaint.setColor(Color.argb(0xcc, 0xcc, 0xcc, 0xcc));//非当前歌词词句颜色

float currentY = heightSign;
//画出本句之前的歌词
for (int i = index - 1; i >= 0; i--) {
currentY -= LYRIC_ONE_HEIGHT;
if (i == index - 1) {
canvas.drawText(lyricBeanList.get(i).getLyric(), mWidth / 2,
currentY, prePaint);
} else {
canvas.drawText(lyricBeanList.get(i).getLyric(), mWidth / 2,
currentY, mPaint);
}
}
currentY = heightSign;
//画出本句之后的歌词
for (int i = index + 1; i < lyricBeanList.size(); i++) {
currentY += LYRIC_ONE_HEIGHT;
if (i == index + 1) {
canvas.drawText(lyricBeanList.get(i).getLyric(), mWidth / 2,
currentY, nextPaint);
} else {
canvas.drawText(lyricBeanList.get(i).getLyric(), mWidth / 2,
currentY, mPaint);
}
}
}

public void setLyricList(List<LyricBean> lyricBeanList) {
this.lyricBeanList = lyricBeanList;
}

public void setStart(boolean start) {
this.start = start;
}

public boolean getStart() {
return this.start;
}

public void setIndex(int index) {
if (this.index != index) {
this.index = index;
heightSign = mHeight / 2 + LYRIC_ONE_HEIGHT / 2;
}
}

private class UpdateThread extends Thread {
@Override
public void run() {
while (true) {
if (start && lyricBeanList.size() > index + 1) {
if (heightSign > mHeight / 2f - LYRIC_ONE_HEIGHT / 2f) {
heightSign -= LYRIC_ONE_HEIGHT /
(float)(lyricBeanList.get(index + 1).getLyricTime()
- lyricBeanList.get(index).getLyricTime()) * 50;
}
postInvalidate();
}
try {
sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mWidth = w;
mHeight = h;
heightSign = h / 2f + LYRIC_ONE_HEIGHT / 2f;
}


}

LyricBean.java如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import java.util.List;  

/**
* 一句歌词
*
* @author HLP
*/
public class LyricBean {

/** 一句歌词 */
private String lyric;
/** 本句歌词开始的时间 */
private int lyricTime;
/** 本句歌词每个字的持续时间 ,构成数组 */
private List<Integer> wordsTime;

public LyricBean(String lyric, int lyricTime, List<Integer> wordsTime) {
super();
this.lyric = lyric;
this.lyricTime = lyricTime;
this.wordsTime = wordsTime;
}

public String getLyric() {
return lyric;
}

public void setLyric(String lyric) {
this.lyric = lyric;
}

public int getLyricTime() {
return lyricTime;
}

public void setLyricTime(int lyricTime) {
this.lyricTime = lyricTime;
}

public List<Integer> getWordsTime() {
return wordsTime;
}

public void setWordsTime(List<Integer> wordsTime) {
this.wordsTime = wordsTime;
}

}

有个模拟【天天动听】的项目在GitHub上,需要手机中有MP3歌曲和天天动听格式的歌词,
歌曲和歌词文件名一致,查看请点击