自定義View強(qiáng)勢(shì)來(lái)襲绳姨,用自定義View實(shí)現(xiàn)歌詞顯示控件上篇之實(shí)現(xiàn)歌詞文件解析

已經(jīng)有兩個(gè)月時(shí)間沒(méi)有發(fā)表新文章了,從開(kāi)始發(fā)表文章以來(lái)阔挠,常常會(huì)有線上的"簡(jiǎn)友"通過(guò)QQ和微信與我進(jìn)行一些技術(shù)上溝通和交流飘庄,我也收獲良多,幾乎每次到最后购撼,我都會(huì)厚著臉皮說(shuō):"多多關(guān)注哦跪削,近期會(huì)有新文章要發(fā)表呢!"迂求。奈何碾盐,每一次當(dāng)我熱情高漲準(zhǔn)備在近期發(fā)表文章的時(shí)候,都因各種各樣的事情耽擱了揩局,于是乎毫玖,我一次又一次爽約了。的確凌盯,最近工作確實(shí)比較忙付枫,幾乎每一天都需要加班,難得某個(gè)周末的下午有點(diǎn)閑暇時(shí)間驰怎,我都泡在了球場(chǎng)上阐滩,熱愛(ài)籃球是其一,最重要的是做我們這行的县忌,由于工作性質(zhì)叶眉,每周一定的運(yùn)動(dòng)是非常必要的,身體是革命的本錢不是嘛芹枷! 不過(guò)衅疙,我可并沒(méi)有因?yàn)槊β稻蜁?huì)在閑暇時(shí)候放縱自己,我依然堅(jiān)持著自己的夢(mèng)想鸳慈,我一直在思考分享一些比較受用的功能效果饱溢,我也爭(zhēng)取把每一個(gè)Demo做到最好!

明天就是八月一號(hào)了走芋,想著要在八月第一天有一個(gè)良好的開(kāi)頭绩郎,也就寫(xiě)下了這篇文章潘鲫。

這次我要向大家分享的是一個(gè)歌詞控件,其實(shí)肋杖,也是我畢業(yè)設(shè)計(jì)中的一部分溉仑。起初我是用ScrollView嵌套TextView,再結(jié)合我的上一篇文章SpannableString來(lái)實(shí)現(xiàn)的状植,Demo其實(shí)我早早地就將放在github上浊竟,不知道有沒(méi)有朋友有留心看到,但是總感覺(jué)用著不是很流暢津畸,而且不容易加入一些自定義內(nèi)容振定,所以一直不好下筆,也不好向大家分享Demo肉拓。不過(guò)后频,有興趣的朋友可以看一下(下載地址),個(gè)人感覺(jué)還是挺有創(chuàng)意的暖途,嘻嘻1跋А(害羞臉~)。

用ScrollView嵌套TextView實(shí)現(xiàn)LyricView效果圖

效果圖是舊版本哦驻售,記住露久,是舊版本! 通過(guò)自定義View實(shí)現(xiàn)的"進(jìn)階版"LyricView功能更強(qiáng)大芋浮,體驗(yàn)效果更佳,能夠?qū)崿F(xiàn)歌詞滑動(dòng)查看壳快,當(dāng)前播放位置高亮顯示纸巷,滑動(dòng)到指定位置并播放等等,總的來(lái)說(shuō)眶痰,大致和網(wǎng)易云音樂(lè)的歌詞顯示效果一樣瘤旨。

考慮到歌詞顯示控件涉及到歌詞解析,自定義控件的實(shí)現(xiàn)等等諸多方面竖伯,可能文章的篇幅上會(huì)比較冗長(zhǎng)存哲,同時(shí)也為了方便"簡(jiǎn)友"們能夠根據(jù)自己的需求和愛(ài)好各取所需。我也就仿著我之前寫(xiě)的文章《像360懸浮窗那樣七婴,用WindowManager實(shí)現(xiàn)炫酷的懸浮迷你音樂(lè)盒》那樣祟偷,將"用自定義View實(shí)現(xiàn)歌詞顯示控件"這篇文章也分成上、下兩篇打厘,分別是《用自定義View實(shí)現(xiàn)歌詞顯示控件上篇之實(shí)現(xiàn)歌詞文件解析》《用自定義View實(shí)現(xiàn)歌詞顯示控件下篇之自定義LyricView的實(shí)現(xiàn)》修肠。而今天將要分享的是上篇,主要講解關(guān)于*.lrc文件的解析户盯,內(nèi)容偏理論嵌施,所以本章最后我也不會(huì)附上Demo饲化,至于下篇我會(huì)盡快整理分享出來(lái),屆時(shí)上下篇的Demo我會(huì)整合在一起共享出來(lái)吗伤。好吧吃靠,進(jìn)入正題:

首先,了解歌詞文件的組成

寫(xiě)過(guò)音樂(lè)播放器的朋友也應(yīng)該都會(huì)去了解過(guò)歌詞文件的規(guī)范格式足淆,既然是歌詞顯示控件巢块,就必然需要好好了解歌詞文件的組成規(guī)范,才能準(zhǔn)確無(wú)誤的解析歌詞文件缸浦,獲得與我有用的信息夕冲。

[ti:一個(gè)人的北京]
[ar:好妹妹樂(lè)隊(duì)]
[al:南北]
[by:]
[offset:0]
[00:00.10]一個(gè)人的北京 - 好妹妹樂(lè)隊(duì)
[00:00.20]詞:秦昊
[00:00.30]曲:秦昊
[00:00.40]
[00:30.16]你有多久沒(méi)有看到 滿天的繁星
[00:37.34]城市夜晚虛偽的光明 遮住你的眼睛
[00:44.40]連周末的電影 也變得不再有趣
[00:51.71]疲憊的日子里 有太多的問(wèn)題
[00:59.21]
[01:00.96]你有多久單身一人 不再去旅行
[01:08.20]習(xí)慣下班回到家里 冷冰冰的空氣
[01:15.58]愛(ài)情這東西 你已經(jīng)不再有勇氣
[01:22.64]情歌有多動(dòng)聽(tīng) 你就有多懷疑
[01:30.60]許多人來(lái)來(lái)去去 相聚又別離
[01:38.29]也有人喝醉哭泣 在一個(gè)人的北京
[01:45.16]也許我成功失意 慢慢的老去
[01:52.76]能不能讓我留下片刻的回憶
[01:58.95]
[01:59.67]許多人來(lái)來(lái)去去 相聚又別離
[02:07.23]也有人匆匆逃離 這一個(gè)人的北京
[02:14.30]也許有一天我們 一起離開(kāi)這里
[02:21.86]離開(kāi)了這里 在晴朗的天氣
[02:28.38]
[02:58.98]你有多久單身一人 不再去旅行
[03:06.36]習(xí)慣下班回到家里 冷冰冰的空氣
[03:13.55]愛(ài)情這東西 你已經(jīng)不再有勇氣
[03:20.69]情歌有多動(dòng)聽(tīng) 你就有多懷疑
[03:28.53]許多人來(lái)來(lái)去去 相聚又別離
[03:36.22]也有人喝醉哭泣 在一個(gè)人的北京
[03:43.28]也許我成功失意 慢慢的老去
[03:50.82]能不能讓我留下片刻的回憶
[03:57.64]許多人來(lái)來(lái)去去 相聚又別離
[04:05.25]也有人匆匆逃離 這一個(gè)人的北京
[04:12.31]也許有一天我們 一起離開(kāi)這里
[04:19.88]離開(kāi)了這里 在晴朗的天氣
[04:26.62]許多人來(lái)來(lái)去去 相聚又別離
[04:34.24]也有人匆匆逃離 這一個(gè)人的北京
[04:41.37]也許有一天我們 一起離開(kāi)這里
[04:48.87]離開(kāi)了這里 在晴朗的天氣
[04:55.08]
[04:56.27]讓我擁抱你 在晴朗的天氣

這如上述文本顯示,是我在QQ音樂(lè)中下載的歌詞文件裂逐,并用文本方式打開(kāi)看到的一個(gè)結(jié)果歹鱼。其實(shí),所有歌詞文件(*.lrc)都是以一個(gè)標(biāo)準(zhǔn)來(lái)進(jìn)行制作的卜高,就如同上述文件一樣由以"[ti:"開(kāi)頭的標(biāo)題弥姻、以"[ar:"開(kāi)頭的歌手、以"[al:"開(kāi)頭的專輯掺涛、以"[by:"開(kāi)頭的制作庭敦、以"[offset:"開(kāi)頭的時(shí)間偏移量和以"[mm:ss.ms]"開(kāi)頭的歌詞信息組成,歌詞信息則是由開(kāi)始時(shí)間(分:秒.毫秒)和歌詞內(nèi)容兩部分組成薪缆。

接著秧廉,解析歌詞文件

既然了解了歌詞文件的組成部分,那么解析歌詞文件也就不是一件困難的事情了拣帽,就是簡(jiǎn)單的文件內(nèi)容讀忍鄣纭:首先獲取*.lrc歌詞文件的二進(jìn)制流InputStream,再又轉(zhuǎn)換成字符流(注意:轉(zhuǎn)化成字符流的時(shí)候需要選擇編碼减拭,經(jīng)過(guò)我多次嘗試蔽豺,發(fā)現(xiàn)好像QQ音樂(lè)的歌詞文件需要用"GBK"解碼,也不清楚會(huì)不會(huì)有具體的判別方式拧粪,有了解的朋友希望可以在留言區(qū)和大伙兒分享)修陡,然后再調(diào)用BufferedReader的readLine()方法逐行讀取文件內(nèi)容,就能獲得文件內(nèi)容了可霎,在這里有一點(diǎn)需要注意的是魄鸦,各種流在使用結(jié)束后一定要調(diào)用close()方法關(guān)閉。下面就是實(shí)現(xiàn)歌詞文件的解析工作:

首先癣朗,需要準(zhǔn)備兩個(gè)類主要用于歌詞解析結(jié)果的緩存:LyricInfo(歌詞信息:包含標(biāo)題号杏、歌手、專輯等等)和LineInfo(歌詞行信息:包含行開(kāi)始時(shí)間和歌詞行內(nèi)容):

LyricInfo 歌詞文件信息

class LyricInfo {
    List<LineInfo> song_lines;  

    String song_artist;  // 歌手
    String song_title;  // 標(biāo)題
    String song_album;  // 專輯

    long song_offset;  // 偏移量
}

LineInfo 歌詞行信息

class LineInfo {
    String content;  // 歌詞內(nèi)容
    long start;  // 開(kāi)始時(shí)間
}

解析歌詞文件源碼

/**
 * 初始化歌詞信息
 * @param inputStream  歌詞文件的流信息
 * */
  private void setupLyricResource(InputStream inputStream, String charsetName) {
    if(inputStream != null) {
        try {
            LyricInfo lyricInfo = new LyricInfo();
            lyricInfo.song_lines = new ArrayList<>();
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream, charsetName);
            BufferedReader reader = new BufferedReader(inputStreamReader);
            String line = null;
            while((line = reader.readLine()) != null) {
                analyzeLyric(lyricInfo, line);
            }
            reader.close();
            inputStream.close();
            inputStreamReader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

 /**
 * 逐行解析歌詞內(nèi)容
 * */
private void analyzeLyric(LyricInfo lyricInfo, String line) {
    int index = line.lastIndexOf("]");
    if(line != null && line.startsWith("[offset:")) {
        // 時(shí)間偏移量
        String string = line.substring(8, index).trim();
        lyricInfo.song_offset = Long.parseLong(string);
        return;
    }
    if(line != null && line.startsWith("[ti:")) {
        // title 標(biāo)題
        String string = line.substring(4, index).trim();
        lyricInfo.song_title = string;
        return;
    }
    if(line != null && line.startsWith("[ar:")) {
        // artist 作者
        String string = line.substring(4, index).trim();
        lyricInfo.song_artist = string;
        return;
    }
    if(line != null && line.startsWith("[al:")) {
        // album 所屬專輯
        String string = line.substring(4, index).trim();
        lyricInfo.song_album = string;
        return;
    }
    if(line != null && line.startsWith("[by:")) {
        return;
    }
    if(line != null && index == 9 && line.trim().length() > 10) {
        // 歌詞內(nèi)容
        LineInfo lineInfo = new LineInfo();
        lineInfo.content = line.substring(10, line.length());
        lineInfo.start = measureStartTimeMillis(line.substring(0, 10));
        lyricInfo.song_lines.add(lineInfo);
    }
}


/**
 * 從字符串中獲得時(shí)間值
 * */
private long measureStartTimeMillis(String str) {
    long minute = Long.parseLong(str.substring(1, 3));
    long second = Long.parseLong(str.substring(4, 6));
    long millisecond = Long.parseLong(str.substring(7, 9));
    return millisecond + second * 1000 + minute * 60 * 1000;
}
最后,驗(yàn)證解析效果

完成歌詞解析盾致,接下來(lái)就是驗(yàn)證歌詞解析的一個(gè)實(shí)際效果的時(shí)候了:

File file = new File(Constant.lyricPath + "一個(gè)人的北京 - 好妹妹樂(lè)隊(duì).lrc");
    if (file != null && file.exists()) {
        try {
            setupLyricResource(new FileInputStream(file), "GBK");
            StringBuffer stringBuffer = new StringBuffer();
            if(lyricInfo != null && lyricInfo.song_lines != null) {
                int size = lyricInfo.song_lines.size();
                for (int i = 0; i < size; i ++) {
                    stringBuffer.append(lyricInfo.song_lines.get(i).content + "\n");
                }
                textView.setText(stringBuffer.toString());
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }
歌詞解析效果圖

關(guān)于歌詞解析的內(nèi)容到這里就結(jié)束了主经,希望大家能夠多多關(guān)注哦!

作者申明:如果文中有表述不當(dāng)或闡述錯(cuò)誤的地方庭惜,還望正在看文章的您可以幫忙指出罩驻,有疑惑也可以在評(píng)論區(qū)提問(wèn)或者私信,期待您的意見(jiàn)和建議护赊,歡迎關(guān)注交流惠遏。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市骏啰,隨后出現(xiàn)的幾起案子节吮,更是在濱河造成了極大的恐慌,老刑警劉巖判耕,帶你破解...
    沈念sama閱讀 212,454評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件透绩,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡壁熄,警方通過(guò)查閱死者的電腦和手機(jī)帚豪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)草丧,“玉大人狸臣,你說(shuō)我怎么就攤上這事〔矗” “怎么了烛亦?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,921評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)懂拾。 經(jīng)常有香客問(wèn)我煤禽,道長(zhǎng),這世上最難降的妖魔是什么委粉? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,648評(píng)論 1 284
  • 正文 為了忘掉前任呜师,我火速辦了婚禮娶桦,結(jié)果婚禮上贾节,老公的妹妹穿的比我還像新娘。我一直安慰自己衷畦,他們只是感情好栗涂,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著祈争,像睡著了一般斤程。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,950評(píng)論 1 291
  • 那天忿墅,我揣著相機(jī)與錄音扁藕,去河邊找鬼。 笑死疚脐,一個(gè)胖子當(dāng)著我的面吹牛亿柑,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播棍弄,決...
    沈念sama閱讀 39,090評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼望薄,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了呼畸?” 一聲冷哼從身側(cè)響起痕支,我...
    開(kāi)封第一講書(shū)人閱讀 37,817評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蛮原,沒(méi)想到半個(gè)月后卧须,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,275評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡瞬痘,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評(píng)論 2 327
  • 正文 我和宋清朗相戀三年故慈,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片框全。...
    茶點(diǎn)故事閱讀 38,724評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡察绷,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出津辩,到底是詐尸還是另有隱情拆撼,我是刑警寧澤,帶...
    沈念sama閱讀 34,409評(píng)論 4 333
  • 正文 年R本政府宣布喘沿,位于F島的核電站闸度,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏蚜印。R本人自食惡果不足惜莺禁,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評(píng)論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望窄赋。 院中可真熱鬧哟冬,春花似錦、人聲如沸忆绰。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,815評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)错敢。三九已至翰灾,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背纸淮。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,043評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工平斩, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人咽块。 一個(gè)月前我還...
    沈念sama閱讀 46,503評(píng)論 2 361
  • 正文 我出身青樓双戳,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親糜芳。 傳聞我的和親對(duì)象是個(gè)殘疾皇子飒货,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評(píng)論 2 350

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