你們好,咱們是愛學啊,今天給你們帶來一篇關於LRC歌詞原理和在Android上如何實現歌詞逐行滾動的效果,本文來自【Android開發項目實戰個人云音樂】課程;逐字滾動下一篇文章講解。java
相信你們都懂一張圖賽過千言萬語。canvas
效果和如今市面上大部分播放器差很少,固然若是要運用到商業項目中,確定還須要進行一些優化,例如:滾動效果有彈性,字體大小,字體顏色等。微信
LRC是英文Lyric(歌詞)的縮寫,經常使用做逐行歌詞擴展名。他是純文本文件,格式簡單,能實現歌詞逐行滾動;固然目前業界大部分播放器都是在他的基礎上定製了,但基本原理同樣,當學完咱們這篇文章後,你們也能夠根據本身的需求定製。架構
在實現歌詞功能前,確定須要搞明白LRC歌詞格式,例如:咱們找了一段LRC歌詞:ide
[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語言。code
讀取該文件每一行,而後用]拆分,第二部分就是歌詞,第一部分繼續用:拆分,而後將三部分轉爲毫秒;最後將這些信息保存到對象上。
固然爲了之後更好的擴展,由於歌詞格式不少,能夠進行一些架構:
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。