摘 要: 基于Android平臺(tái)開(kāi)發(fā)音樂(lè)播放器。該播放器主要實(shí)現(xiàn)了SD卡掃描、后臺(tái)播放、歌手與專(zhuān)輯篩選,歌曲列表管理、歌詞同步滾動(dòng)顯示、播放模式選擇、皮膚更換、網(wǎng)絡(luò)下載、桌面Widget等功能。對(duì)Android應(yīng)用程序的開(kāi)發(fā)環(huán)境及工具作了簡(jiǎn)單介紹,詳細(xì)介紹了音樂(lè)播放器軟件界面布局方式、自動(dòng)音樂(lè)掃描機(jī)制、歌詞同步實(shí)現(xiàn)算法以及歌詞的搜索與下載等功能模塊的設(shè)計(jì)與實(shí)現(xiàn)。對(duì)歌詞同步滾動(dòng)顯示進(jìn)行了透徹分析。該音樂(lè)播放器通過(guò)了Android智能手機(jī)運(yùn)行測(cè)試,具有較好的集成度和良好的穩(wěn)定性。
關(guān)鍵詞: Android;Java;音樂(lè)播放;歌詞同步
隨著科技發(fā)展的日新月異,人們對(duì)移動(dòng)設(shè)備的需求越來(lái)越高,手機(jī)已不只是通信工具,而是一個(gè)多媒體平臺(tái)。Android是Google公司開(kāi)發(fā)的基于Linux平臺(tái)的開(kāi)源的移動(dòng)終端智能操作系統(tǒng)[1]。Android系統(tǒng)由操作系統(tǒng)、用戶界面和應(yīng)用程序組成,允許開(kāi)發(fā)人員自由獲取和修改源代碼。Android的發(fā)布大大豐富了各種手持式設(shè)備軟件的功能[2]。
本文基于Android平臺(tái)開(kāi)發(fā)音樂(lè)播放器,選擇開(kāi)發(fā)個(gè)性的播放軟件,摒棄單方面追求花哨而帶來(lái)的系統(tǒng)資源浪費(fèi),將各種性能優(yōu)化,繼承播放器的常用功能,滿足大多數(shù)用戶的娛樂(lè)需求。該播放器實(shí)現(xiàn)SD卡掃描、后臺(tái)播放、歌手與專(zhuān)輯篩選,歌曲列表管理、歌詞同步滾動(dòng)顯示、播放模式選擇、皮膚更換、網(wǎng)絡(luò)下載等功能。此外,還實(shí)現(xiàn)桌面的Widget功能,使用戶在不打開(kāi)該軟件的同時(shí),就可以一鍵聽(tīng)歌,極大地優(yōu)化了用戶體驗(yàn)。
1 Android簡(jiǎn)介
Android系統(tǒng)分為Applications、Application Framework、Libraries、Android Runtime、Linux Kernel 5大層[3]。本播放軟件屬于應(yīng)用軟件,只對(duì)Applications應(yīng)用層程序的探討,對(duì)具體壓縮算法不作深究。
1.1 Android基本組件
Android應(yīng)用程序的組件主要有4個(gè),針對(duì)智能手機(jī)的諸多突發(fā)情形,都做出了相應(yīng)的處理操作[4]。
(1)Activity:是應(yīng)用程序最基本的組件。應(yīng)用程序的每個(gè)頁(yè)面都由各種Activity構(gòu)成。它是一種可視化的、直接與用戶接觸的界面元素。
?。?)Service:是一種服務(wù)組件,運(yùn)行于程序的后臺(tái)。該組件對(duì)用戶是不可見(jiàn)的,在后臺(tái)提供程序的托管運(yùn)行。
?。?)ContentProvider:是一種內(nèi)容提供者組件。該組件能夠?qū)崿F(xiàn)應(yīng)用程序之間的數(shù)據(jù)共享,并能夠監(jiān)聽(tīng)其共享數(shù)據(jù)的變化。
?。?)BroadcastReceiver:實(shí)現(xiàn)應(yīng)用程序內(nèi)部數(shù)據(jù)的傳遞,也能實(shí)現(xiàn)事件的先后順序觸發(fā)。
1.2 開(kāi)發(fā)工具
軟件開(kāi)發(fā)使用Eclipse軟件,使用Android SDK、ADT的支持,JDK開(kāi)發(fā)環(huán)境,使用Java語(yǔ)言作為開(kāi)發(fā)語(yǔ)言,基于C/S開(kāi)發(fā)模式。使用Emulator調(diào)試工具,調(diào)試工具提供了斷點(diǎn)調(diào)試,文件管理,電話短信模擬,在軟件開(kāi)發(fā)過(guò)程中提供了極大的方便。
2 軟件核心功能
該部分詳細(xì)介紹了播放界面的布局方式、音樂(lè)列表自動(dòng)掃描原理、播放時(shí)歌詞同步滾動(dòng)實(shí)現(xiàn)機(jī)制、歌詞搜索與下載機(jī)制。
2.1 主頁(yè)面布局
軟件的主播放界面采用線性布局與層疊布局的結(jié)合,布局中使用了Android的系統(tǒng)控件和自定義的控件,豐富了頁(yè)面元素,并對(duì)每個(gè)控件進(jìn)行了布局設(shè)置,下面對(duì)應(yīng)播放主界面的布局:
//線性布局方式
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:lrcview="http://schemas.android.com/apk/res/com.gao.mymediaplayer01"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<FrameLayout //層疊布局方式
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ImageView //圖片控件
android:id="@+id/background"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_gravity="fill"/>
//下面是自定義歌詞控件的布局控制
<com.gao.mymediaplayer01.LrcView
//自定義歌詞控件類(lèi)
android:id="@+id/lrcTextView"//控件的唯一標(biāo)識(shí)(Id)
android:layout_width="fill_parent"
//設(shè)置控件的寬度填充父控件
android:layout_height="160dip"
//設(shè)置控件的高度為特定值
android:gravity="center"
//設(shè)置控件內(nèi)容的對(duì)齊方式為居中
android:layout_gravity="center_horizontal"
//控件的對(duì)齊方式為水平居中
android:layout_marginTop="70dip"
//控件垂直方向上距離頂部的距離
/>........................其他控件........................
上面是頁(yè)面布局的部分代碼,最后一個(gè)控件com.gao.mymediaplayer01.LrcView使用的是自定義的控件,目的是顯示歌詞信息并能夠根據(jù)歌曲當(dāng)前播放時(shí)間匹配歌詞的當(dāng)前行索引,實(shí)現(xiàn)歌詞的實(shí)時(shí)動(dòng)態(tài)刷新顯示。實(shí)現(xiàn)的效果如圖1所示。
2.2 音樂(lè)掃描
Android系統(tǒng)提供了一種類(lèi)似關(guān)系表的結(jié)構(gòu)來(lái)把應(yīng)用程序的數(shù)據(jù)暴露給外界,并把每個(gè)這種表使用唯一的標(biāo)識(shí)符URI來(lái)標(biāo)識(shí)[2]。Android系統(tǒng)對(duì)外部存儲(chǔ)設(shè)備的媒體文件進(jìn)行了統(tǒng)一管理,把每個(gè)音樂(lè)文件的ID、時(shí)長(zhǎng)、藝術(shù)家等相關(guān)信息全部存放在這個(gè)表中,使用Contentprovider來(lái)訪問(wèn)這個(gè)唯一的標(biāo)識(shí)符URI便可以查詢到在用戶的SD卡中的所有的音樂(lè)文件,實(shí)現(xiàn)代碼如下:
musicCursor=this.getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
new String[]{MediaStore.Audio.Media.TITLE,//歌曲標(biāo)題
MediaStore.Audio.Media.DURATION,//歌曲時(shí)長(zhǎng)
MediaStore.Audio.Media.ARTIST,//歌曲的作者
MediaStore.Audio.Media._ID,
//歌曲在SD卡上的唯一標(biāo)識(shí)
MediaStore.Audio.Media.DISPLAY_NAME,
//歌曲顯示的名字
MediaStore.Audio.Media.DATA//歌曲文件的路徑
},null,null,null);
根據(jù)查詢條件可以得到所有歌曲的游標(biāo)指針,這里的查詢條件可以設(shè)置指定的藝術(shù)家和指定的專(zhuān)輯,從而可以查詢指定的藝術(shù)家和專(zhuān)輯的特定歌曲列表。將得到的游標(biāo)數(shù)據(jù)傳遞到ListAdapter適配器中,其中可以設(shè)置列表項(xiàng)顯示的內(nèi)容,之后為這個(gè)ListView控件設(shè)置單擊事件。
2.3 歌詞同步滾動(dòng)實(shí)現(xiàn)機(jī)制
音樂(lè)播放時(shí)實(shí)現(xiàn)歌詞同步滾動(dòng)顯示是本音樂(lè)播放器的一個(gè)特色。下面詳細(xì)介紹實(shí)現(xiàn)歌詞同步滾動(dòng)的具體流程:
?。?)歌詞LRC文件一般存放在與該歌曲相同的位置,通過(guò)歌曲在SD卡上的DATA屬性獲得LRC文件的位置,使用輸入緩沖流BufferedReader進(jìn)行讀取,關(guān)鍵是每一次讀取歌詞文件的一行,因?yàn)楦柙~文件的每一行是一個(gè)或者多個(gè)時(shí)刻和歌詞內(nèi)容的連接,這里把每行歌詞抽象為一個(gè)對(duì)象,整個(gè)歌詞文件看成是所有對(duì)象的List集合,每個(gè)對(duì)象是由時(shí)間屬性和歌詞內(nèi)容屬性共同組成,在讀取每一行歌詞并將其轉(zhuǎn)換成歌詞類(lèi)的一個(gè)實(shí)例時(shí),對(duì)不同的表示形式作了不同的處理:
[00:25.93]和[00:25]兩種時(shí)間形式的處理。對(duì)其統(tǒng)一格式后判斷時(shí)間段的長(zhǎng)度,采用不同的函數(shù)處理。在實(shí)際使用時(shí)將其轉(zhuǎn)換為毫秒保存到對(duì)象的時(shí)間的私有變量中,內(nèi)容保存到LRC內(nèi)容的成員變量中。
對(duì)于在一行歌詞中多個(gè)時(shí)間段表示同一種歌詞內(nèi)容的情形,首先對(duì)整行歌詞中的字符“]”統(tǒng)一替換為某個(gè)特殊的字符,將整行內(nèi)容根據(jù)這個(gè)特殊字符進(jìn)行分割得到string類(lèi)型的數(shù)組:String[]splitLrc_data=str.split("@");可以得到數(shù)組長(zhǎng)度減一個(gè)數(shù)量的歌詞對(duì)象,每個(gè)歌詞對(duì)象時(shí)間域?yàn)榉指畹玫降臄?shù)組的內(nèi)容,內(nèi)容域都是數(shù)組最后一個(gè)元素的值,最后將所有這些歌詞對(duì)象存放到List<LrcContent>當(dāng)中。
?。?)對(duì)List歌詞對(duì)象按照其時(shí)間變量進(jìn)行排序,排序采用冒泡排序算法,其核心代碼如下:
for(int i=1;i<=count-1;i++)
for(int j=0;j<count-i;j++)
{//排序依據(jù)是歌詞對(duì)象的時(shí)間變量
if(LrcList.get(j).getLrc_time()>LrcList.get(j+1).getLrc_time())
{
LrcContent tempLrcContent=LrcList.set(j,LrcList.get(j+1));
LrcList.set(j+1,tempLrcContent);
}}
?。?)利用冒泡排序算法最終得到按時(shí)間先后排好序的歌詞對(duì)象的List,根據(jù)歌曲當(dāng)前播放時(shí)間選擇當(dāng)前需要顯示的歌詞行的索引,具體操作如下。
當(dāng)前歌曲時(shí)間小于第一個(gè)歌詞對(duì)象的時(shí)間時(shí),設(shè)定要顯示歌詞的行的索引為1;
當(dāng)前歌曲時(shí)間大于第一個(gè)歌詞對(duì)象的時(shí)間時(shí),要循環(huán)判斷出當(dāng)前歌詞時(shí)間大于第N個(gè)歌詞對(duì)象的時(shí)間并且要小于第N+1個(gè)歌詞對(duì)象的時(shí)間,設(shè)定要顯示的歌詞的行的索引為N;
當(dāng)前歌曲時(shí)間大于最后一個(gè)歌詞行的時(shí)間時(shí),設(shè)定要顯示的歌詞行的索引為歌詞文件的數(shù)量。
?。?)最后,得到當(dāng)前顯示歌詞的索引后,使用自定義文本控件高亮顯示當(dāng)前歌詞行,其余歌詞行非高亮顯示。在后臺(tái)Service中設(shè)定刷新頻率為50 ms,每50 ms獲得當(dāng)前歌曲時(shí)間進(jìn)度,更新當(dāng)前行的索引,獲得一個(gè)時(shí)刻的當(dāng)前行的索引后,從Service中利用廣播機(jī)制將其發(fā)送到前臺(tái)Activity中,在Activity接收到當(dāng)前索引后,將自定義的歌詞控件重新繪出。
設(shè)置lrcView.setIndex(lrcIndex)后,使用lrcView.invalidate()強(qiáng)制使歌詞控件重新繪畫(huà),此時(shí)繪出的高亮行為當(dāng)前索引行,且顯示在屏幕的中央,其余行顯示為非高亮行,繪畫(huà)歌詞的算法核心代碼如下:
canvas.drawText(lrcList.get(Index).getLrc_body(),width/2,high/2,
CurrentPaint);
float tempY=high/2;//屏幕垂直方向中央的高度
//畫(huà)出本句之前的句子
for(int i=Index-1;i>=0;i--){
tempY=tempY-TextHigh;
canvas.drawText(lrcList.get(i).getLrc_body(),width/2,tempY,NotCurrentPaint);}
tempY=high/2;
//畫(huà)出本句之后的句子
for(int i=Index+1;i<=lrcList.size()-1;i++){
tempY=tempY+TextHigh;
canvas.drawText(lrcList.get(i).getLrc_body(),width/2,tempY,NotCurrentPaint);}
按照上述步驟,在后臺(tái)Service中設(shè)定刷新頻率(一般為50~200 ms)可以實(shí)現(xiàn)動(dòng)態(tài)的顯示歌詞。歌詞滾動(dòng)效果如圖1所示。
2.4 歌詞的搜索與下載
要下載一首歌的歌詞信息,應(yīng)該由這首歌曲的歌手和歌曲名共同決定,所以利用當(dāng)前播放歌曲的歌手和歌曲名稱(chēng)作為參數(shù)進(jìn)行歌詞的搜索,這里使用百度音樂(lè)盒提供的歌詞服務(wù)器來(lái)進(jìn)行下載。下載流程如下:
?。?)首先將歌曲的歌手和歌曲名稱(chēng)進(jìn)行UTF-8編碼的轉(zhuǎn)換如下:
titleName=URLEncoder.encode(titleName,"UTF-8");
singerName=URLEncoder.encode(singerName,"UTF-8");
?。?)其次,將參數(shù)傳遞到搜索鏈接中:
strUrl="http://box.zhangmen.baidu.com/x?op=12&count=1&title="+titleName+"$$"+singerName+"$$$$";
此鏈接指向的是一個(gè)xml類(lèi)型的文件,該文件包含對(duì)該歌曲及歌詞等信息的描述,使用I/O流讀取該文件,如果該文件內(nèi)容不為空,可以從中獲取到該歌詞在服務(wù)器中的Id(即LyricId),根據(jù)這個(gè)Id,如果這個(gè)Id不為空,進(jìn)而可以得到該歌詞文件的URL鏈接地址:
lyricURLStr="http://box.zhangmen.baidu.com/bdlrc/"+lyricId/100+"/"+lyricId+".lrc";
根據(jù)該URL使用I/O流將歌詞文件下載到本地就可以完成歌詞的下載。
?。?)最后,歌詞下載完成后,獲取歌詞的保存路徑,調(diào)用解析歌詞文件的方法進(jìn)行解析實(shí)現(xiàn)歌詞的滾動(dòng)顯示。
本文介紹了基于Android平臺(tái)的音樂(lè)播放器的設(shè)計(jì)方案和關(guān)鍵技術(shù)。詳細(xì)介紹了音樂(lè)播放器軟件界面布局方式、自動(dòng)音樂(lè)掃描機(jī)制、歌詞同步實(shí)現(xiàn)算法以及歌詞的搜索與下載等功能模塊的設(shè)計(jì)與實(shí)現(xiàn)。對(duì)歌詞同步滾動(dòng)顯示進(jìn)行了透徹分析。該音樂(lè)播放器集掃描SD卡,音樂(lè)列表顯示、播放、后臺(tái)播放、上一首、下一首、音量調(diào)節(jié)歌手選擇、專(zhuān)輯選擇、最近播放、最經(jīng)常播放、歌詞同步滾動(dòng)顯示、快進(jìn)快退、播放模式選擇、更換皮膚、音樂(lè)文件操作、網(wǎng)絡(luò)下載、桌面Widget等功能于一體,功能較完善。通過(guò)在Android智能手機(jī)對(duì)音樂(lè)播放器進(jìn)行了功能測(cè)試。該音樂(lè)播放器性能良好,運(yùn)行流暢。
參考文獻(xiàn)
[1] 周時(shí)偉,謝維波.基于Android的智能家居終端設(shè)計(jì)與實(shí)現(xiàn)[J].微型機(jī)與應(yīng)用,2012,31(14):10-13.
[2] 曾建平,邵艷潔.Android系統(tǒng)架構(gòu)及應(yīng)用程序開(kāi)發(fā)研究[J].微計(jì)算機(jī)信息,2011,27(9):1-3.
[3] 樊新,高曙.基于智能移動(dòng)終端的安全檢查系統(tǒng)設(shè)計(jì)與實(shí)現(xiàn)[J].微型機(jī)與應(yīng)用,2012,31(20):87-92.
[4] 劉安戰(zhàn),賈曉輝.基于Android的私密短信系統(tǒng)設(shè)計(jì)與實(shí)現(xiàn)[J].微型機(jī)與應(yīng)用,2012,31(17):51-56.