Android項目實戰(zhàn)之高仿網(wǎng)易云音樂LRC歌詞

大家好手负,我們是愛學啊旭从,今天給大家?guī)硪黄P(guān)于LRC歌詞原理和在Android上如何實現(xiàn)歌詞逐行滾動的效果,本文來自【Android開發(fā)項目實戰(zhàn)我的云音樂】課程反浓;逐字滾動下一篇文章講解皇耗。

效果圖

相信大家都懂一張圖勝過千言萬語南窗。

lrc1.gif

<img src="http://7xqoji.com1.z0.glb.clouddn.com/mytest.jpg" width="500" hegiht="313" align=center />

效果和現(xiàn)在市面上大部分播放器差不多,當然如果要運用到商業(yè)項目中郎楼,肯定還需要進行一些優(yōu)化万伤,例如:滾動效果有彈性,字體大小呜袁,字體顏色等敌买。

什么是LRC歌詞

LRC是英文Lyric(歌詞)的縮寫,常用作逐行歌詞擴展名阶界。他是純文本文件虹钮,格式簡單,能實現(xiàn)歌詞逐行滾動膘融;當然目前業(yè)界大部分播放器都是在他的基礎(chǔ)上定制了芙粱,但基本原理一樣,當學完我們這篇文章后氧映,大家也可以根據(jù)自己的需求定制春畔。

LRC歌詞格式

在實現(xiàn)歌詞功能前,肯定需要搞明白LRC歌詞格式,例如:我們找了一段LRC歌詞:

[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]像朵永遠不調(diào)零的花
[00:24.115]陪我經(jīng)過那風吹雨打
[00:27.921]看世事無常
[00:29.653]看滄桑變化
[00:32.576]那些為愛所付出的代價
[00:36.279]是永遠都難忘的啊
[00:40.485]所有真心的癡心的話

可以看到內(nèi)容是用換行符分割的律姨,如果這些數(shù)據(jù)是通過接口返回振峻,而不是直接返回一個LRC文件,那么這里面的換行符應該變?yōu)閈n換行符择份,這一點我們也在課程中講解到了铺韧。

每一行是一句歌詞;每一行歌詞又分為兩部分缓淹,中括號里面是當前這行歌詞的開始時間,格式為分:秒:毫秒塔逃,有些歌詞可能沒有毫秒讯壶,只有秒;歌詞開頭由于部分數(shù)據(jù)稱為LRC元數(shù)據(jù)湾盗,他是用來描述這個歌詞的伏蚊,部分字段解釋如下:

ti:title,標題格粪,通常是歌曲名稱
ar:artist躏吊,藝人名
al:album,專輯名
by:歌詞創(chuàng)建人帐萎,這里是ttpod比伏,指的是天天動聽
total:整首歌曲時長,單位毫秒
offset:時間補償值疆导,單位毫秒赁项,正值表示整體提前,負值相反

前面這些字段根據(jù)不同的播放器可能用的位置不一樣澈段,我們課程中雖然解析了這些字段悠菜,但也沒有用到。

歌詞滾動原理

將每行歌詞前面的時間解析后败富,轉(zhuǎn)為毫秒悔醋,這樣播放器在播放的時候可以獲取到播放時間,然后拿著時間查找當前時間對應哪一行歌詞兽叮,然后在界面上高亮這一行歌詞芬骄,或者做更多的處理,例如:字體增大等操作充择;就實現(xiàn)了歌詞逐行高亮德玫;至于滾動不同的平臺不一樣,滾動思路是:獲取到當前時間所對應哪一行椎麦,然后我們肯定能算出每一行歌詞高度宰僧,所以行*每一行高度就是滾動的高度。

歌詞解析

不同的語言語法不一樣,我們這里先說思路琴儿,我們的實現(xiàn)是Java語言段化。

讀取該文件每一行,然后用]拆分造成,第二部分就是歌詞显熏,第一部分繼續(xù)用:拆分,然后將三部分轉(zhuǎn)為毫秒晒屎;最后將這些信息保存到對象上喘蟆。

當然為了以后更好的擴展,因為歌詞格式很多鼓鲁,可以進行一些架構(gòu):

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的中心高度燥狰,然后在該位置繪制當前行歌詞棘脐,這一步根據(jù)不同的歌詞處理的邏輯也不一樣,但歌詞可分為兩類龙致,一類是逐行蛀缝,一類是逐字,對于逐行來說就直接繪制就行了目代,只是顏色内斯,大小不一樣而已;逐字下一節(jié)講解像啼;然后從當前行歌詞位置像前繪制歌詞俘闯,直到超出View頂部為止,在從當前行歌詞向下歌詞繪制忽冻,直到超出View底部為止真朗;當前你可以使用LinearLayout添加所有歌詞當前容器內(nèi),然后滾動僧诚。

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 精確到字遮婶,歌詞,下一節(jié)講解
    } 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中不同的實現(xiàn)方法滾動方式也不一樣,如果是直接繪制慈省,那么滾動其實就是繪制不同行歌詞臀防,給人的感覺就是滾動了;如果是將所有歌詞添加到容器中,那么就可以使用容器默認的滾動袱衷;對于我們這里的實現(xiàn)滾動其實就是更改lineNumber值捎废,例如;當前l(fā)ineNumber為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核心功能基本就實現(xiàn)完成了,如果要深入學習可以查看我們的【Android開發(fā)項目實戰(zhàn)我的云音樂】課程脱吱,或者在線電子書【電子書】荷腊;同時大家也可以關(guān)注我們的微信公眾號【ixuea666】和Android開發(fā)交流群:702321063。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末急凰,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子猜年,更是在濱河造成了極大的恐慌抡锈,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件乔外,死亡現(xiàn)場離奇詭異床三,居然都是意外死亡,警方通過查閱死者的電腦和手機杨幼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進店門撇簿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人差购,你說我怎么就攤上這事四瘫。” “怎么了欲逃?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵找蜜,是天一觀的道長。 經(jīng)常有香客問我稳析,道長洗做,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任彰居,我火速辦了婚禮诚纸,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘陈惰。我一直安慰自己畦徘,他們只是感情好,可當我...
    茶點故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著旧烧,像睡著了一般影钉。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上掘剪,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天平委,我揣著相機與錄音,去河邊找鬼夺谁。 笑死廉赔,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的匾鸥。 我是一名探鬼主播蜡塌,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼勿负!你這毒婦竟也來了馏艾?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤奴愉,失蹤者是張志新(化名)和其女友劉穎琅摩,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體锭硼,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡房资,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了檀头。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片轰异。...
    茶點故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖暑始,靈堂內(nèi)的尸體忽然破棺而出搭独,到底是詐尸還是另有隱情,我是刑警寧澤廊镜,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布戳稽,位于F島的核電站,受9級特大地震影響期升,放射性物質(zhì)發(fā)生泄漏惊奇。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一播赁、第九天 我趴在偏房一處隱蔽的房頂上張望颂郎。 院中可真熱鬧,春花似錦容为、人聲如沸乓序。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽替劈。三九已至寄雀,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間陨献,已是汗流浹背盒犹。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留眨业,地道東北人急膀。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像龄捡,于是被迫代替她去往敵國和親卓嫂。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,465評論 2 348

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,742評論 25 707
  • 用兩張圖告訴你聘殖,為什么你的 App 會卡頓? - Android - 掘金 Cover 有什么料晨雳? 從這篇文章中你...
    hw1212閱讀 12,699評論 2 59
  • 這一節(jié)我們來講解這個項目所用到的一些技術(shù),以及一些實現(xiàn)的效果圖奸腺,讓大家對該項目有一個整體的認識餐禁,推薦大家收藏該文章...
    愛學啊閱讀 6,958評論 6 86
  • 1、通過CocoaPods安裝項目名稱項目信息 AFNetworking網(wǎng)絡(luò)請求組件 FMDB本地數(shù)據(jù)庫組件 SD...
    陽明先生_X自主閱讀 15,969評論 3 119
  • 我是個超級懷舊的人洋机。 恍恍惚惚又是一個夏天。 高考結(jié)束之后的很多天我都沒有去趟老家洋魂,好像很多次聽到奶奶的嘮叨“考完...
    小千的貓閱讀 214評論 1 3