最近在以QQ音樂為樣闆做一個手機音樂播放器,源碼下篇博文放出。今天我想聊的是這個QQ音樂播放器中歌詞顯示控件的問題,和小夥伴們一起來探讨怎麼實作這個歌詞滾動的效果。OK,廢話不多說,先來看看效果圖:
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIyVGduV2QvwVe0lmdhJ3ZvwFM38CXlZHbvN3cpR2Lc1TPB10QGtWUCpEMJ9CXsxWam9CXwADNvwVZ6l2c052bm9CXUJDT1wkNhVzLcRnbvZ2LcZXUYpVd1kmYr50MZV3YyI2cKJDT29GRjBjUIF2LcRHelR3LcJzLctmch1mclRXY39DN4IzNzUjMyEjMyATM2EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
好,接下來我們就來看看怎麼實作這樣一個效果。本文主要包括如下幾方面内容:
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