Android音樂播放器的開發(fā)實(shí)例

本文將引導(dǎo)大家做一個(gè)音樂播放器沮榜,在做這個(gè)Android開發(fā)實(shí)例的過程中盘榨,能夠幫助大家進(jìn)一步熟悉和掌握學(xué)過的ListView和其他一些組件。為了有更好的學(xué)習(xí)效果蟆融,其中很多功能我們手動(dòng)實(shí)現(xiàn)草巡,例如音樂播放的快進(jìn)快退等。

先欣賞下本實(shí)例完成后運(yùn)行的界面效果:

首先我們建立項(xiàng)目型酥,我使用的SDK是Android2.2的山憨,然后在XML中進(jìn)行布局。

上方是一個(gè)ListView用來顯示我們的音樂列表弥喉,中間是一個(gè)SeekBar可以拖動(dòng)當(dāng)前音樂的播放進(jìn)度郁竟,之所以用SeekBar而不用ProgressBar是因?yàn)槲覀冃枰魳返目爝M(jìn)快退功能,可以拖動(dòng)滑桿改變進(jìn)度由境;還有一個(gè)TextView棚亩,用來顯示當(dāng)前播放歌曲的名字蓖议,時(shí)長等。最下方就是4個(gè)Button了讥蟆,分別是上一曲勒虾,播放(暫停),停止攻询,下一曲从撼。

大家注意盡量不要在布局中出現(xiàn)直接顯示在界面上的文字內(nèi)容,我們把這些內(nèi)容都放在res/values下的strings.xml中钧栖,然后分別引用它們低零,這樣養(yǎng)成良好的習(xí)慣,界面與內(nèi)容分離拯杠,方便調(diào)試和后期維護(hù)等√蜕簦現(xiàn)在我們的界面如下:

然后我們把File Explorer打開,在eclipse的Window -- Show View -- Other --Android --File Explore潭陪。你也可以直接Alt+Shift+Q雄妥。

在mnt/sdcard下面,我們放個(gè)兩三首歌曲依溯,在虛擬機(jī)中暫不支持中文老厌,導(dǎo)入有中文的文件會(huì)報(bào)錯(cuò)的。

接著我們創(chuàng)建一個(gè)類黎炉,做我們播放器的Service類枝秤,我就叫MusicService吧,在里面聲明以下對(duì)象:

Java代碼

publicclassMusicService?{

privatestaticfinalFile?MUSIC_PATH?=?Environment

.getExternalStorageDirectory();//?找到music存放的路徑慷嗜。

publicList?musicList;//?存放找到的所有mp3的絕對(duì)路徑淀弹。

publicMediaPlayer?player;//?定義多媒體對(duì)象

publicintsongNum;//?當(dāng)前播放的歌曲在List中的下標(biāo)

publicString?songName;//?當(dāng)前播放的歌曲名

}

然后我們?nèi)ゼ虞d剛才添加的MP3文件吧,這里的方式多種多樣庆械,我隨便寫一個(gè)簡單的了:

Java代碼

classMusicFilterimplementsFilenameFilter?{

publicbooleanaccept(File?dir,?String?name)?{

return(name.endsWith(".mp3"));//返回當(dāng)前目錄所有以.mp3結(jié)尾的文件

}

}

在MusicService類的無參構(gòu)造函數(shù)中實(shí)例化對(duì)象薇溃,并把這些MP3文件放到musicList中。

Java代碼

publicMusicService()?{

musicList?=newArrayList();

player?=newMediaPlayer();

if(MUSIC_PATH.listFiles(newMusicFilter()).length?>0)?{

for(File?file?:?MUSIC_PATH.listFiles(newMusicFilter()))?{

musicList.add(file.getAbsolutePath());

}

}

}

我們寫個(gè)方法缭乘,來設(shè)置當(dāng)前播放歌曲的名字:(個(gè)人覺得這方法比較笨沐序,但暫時(shí)沒想到別的辦法)

Java代碼

publicvoidsetPlayName(String?dataSource)?{

File?file?=newFile(dataSource);//假設(shè)為D:\\mm.mp3

String?name?=?file.getName();//name=mm.mp3

intindex?=?name.lastIndexOf(".");//找到最后一個(gè).

songName?=?name.substring(0,?index);//截取為mm

}

接下來就是我們Service類的基本方法了,也就是開始堕绩、暫停薄啥、停止、上一首和下一首逛尚。

我們分別使用聲明的多媒體對(duì)象的start、pause刁愿、stop等方法可以完成绰寞。

Java代碼

publicvoidstart()?{

try{

player.reset();//重置多媒體

String?dataSource?=?musicList.get(songNum);//得到當(dāng)前播放音樂的路徑

setPlayName(dataSource);//截取歌名

player.setDataSource(dataSource);//為多媒體對(duì)象設(shè)置播放路徑

player.prepare();//準(zhǔn)備播放

player.start();//開始播放

//setOnCompletionListener?當(dāng)當(dāng)前多媒體對(duì)象播放完成時(shí)發(fā)生的事件

player.setOnCompletionListener(newOnCompletionListener()?{

publicvoidonCompletion(MediaPlayer?arg0)?{

next();//如果當(dāng)前歌曲播放完畢,自動(dòng)播放下一首.

}

});

}catch(Exception?e)?{

Log.v("MusicService",?e.getMessage());

}

}

publicvoidnext()?{

songNum?=?songNum?==?musicList.size()?-1?0:?songNum?+1;

start();

}

publicvoidlast()?{

songNum?=?songNum?==0??musicList.size()?-1:?songNum?-1;

start();

}

publicvoidpause()?{

if(player.isPlaying())

player.pause();

else

player.start();

}

publicvoidstop()?{

if(player.isPlaying())?{

player.stop();

}

}

到此為止我們的Service類就寫完了,接著我們?nèi)ctivity中為各控件綁定事件。

在這個(gè)Activity中滤钱,最難做的一點(diǎn)應(yīng)該就是拖動(dòng)SeekBar的滑桿改變播放進(jìn)度了觉壶,這里我考慮再三,用了一個(gè)Handler類來處理件缸。

Handler在android里負(fù)責(zé)發(fā)送和處理消息铜靶。它的主要用途有:

1.按計(jì)劃發(fā)送消息或執(zhí)行某個(gè)Runnanble(使用POST方法)。

2.從其他線程中發(fā)送來的消息放入消息隊(duì)列中他炊,避免線程沖突(常見于更新UI線程)争剿。

默認(rèn)情況下,Handler接受的是當(dāng)前線程下的消息循環(huán)實(shí)例(使用Handler(Looper looper)痊末、Handler(Looper looper, Handler.Callback callback)可以指定線程)蚕苇,同時(shí)一個(gè)消息隊(duì)列可以被當(dāng)前線程中的多個(gè)對(duì)象進(jìn)行分發(fā)、處理(在UI線程中凿叠,系統(tǒng)已經(jīng)有一個(gè)Activity來處理了涩笤,你可以再起若干個(gè)Handler來處理)。在實(shí)例化Handler的時(shí)候盒件,Looper可以是任意線程的蹬碧,只要有Handler的指針,任何線程也都可以sendMessage炒刁。Handler對(duì)于Message的處理不是并發(fā)的恩沽。一個(gè)Looper 只有處理完一條Message才會(huì)讀取下一條,所以消息的處理是阻塞形式的(handleMessage()方法里不應(yīng)該有耗時(shí)操作切心,可以將耗時(shí)操作放在其他線程執(zhí)行飒筑,操作完后發(fā)送Message(通過sendMessges方法),然后由handleMessage()更新UI)。

聲明以下變量:

Java代碼

privateButton?btnStart,?btnStop,?btnNext,?btnLast;

privateTextView?txtInfo;

privateListView?listView;

privateSeekBar?seekBar;

privateMusicService?musicService;

privateMusicHandler?musicHandler;//?處理改變進(jìn)度條事件

privateMusicThread?musicThread;//?自動(dòng)改變進(jìn)度條的線程

privatebooleanautoChange,?manulChange;//?判斷是進(jìn)度條是自動(dòng)改變還是手動(dòng)改變

privatebooleanisPause;//?判斷是從暫停中恢復(fù)還是重新播放

如有報(bào)錯(cuò)的可以先注釋掉不用管它绽昏,然后在初始化過程中綁定事件协屡。

這是ListView的填充方法:

Java代碼

privatevoidsetListViewAdapter()?{

List>?date?=newArrayList>();

for(String?path?:?musicService.musicList)?{

Map?map?=newHashMap();

File?file?=newFile(path);

map.put("fileName",?file.getName());

date.add(map);

}

SimpleAdapter?adapter?=newSimpleAdapter(this,?date,

android.R.layout.simple_list_item_1,

newString[]?{"fileName"},newint[]?{?android.R.id.text1?});

listView.setAdapter(adapter);

}

SimpleAdapter的構(gòu)造函數(shù)是:

public SimpleAdapter (Context context, List> data, int resource, String[] from, int[] to);

第一個(gè)參數(shù)context全谤,是指在哪個(gè)Activity中顯示肤晓。

第二個(gè)參數(shù)是一個(gè)泛型作為數(shù)據(jù)源,而且每一個(gè)List中的一行就代表著呈現(xiàn)出來的一行认然,Map的鍵就是這一行的列名补憾,值也是有列名的。

第三個(gè)參數(shù)為資源文件卷员,就是說要加載這個(gè)列所需要的視圖資源文件盈匾,我直接引用系統(tǒng)內(nèi)置的資源,如果你想要漂亮的樣式可以自己寫的毕骡。

第四個(gè)參數(shù)是一個(gè)String數(shù)組削饵,主要是將Map對(duì)象中的名稱映射到列名岩瘦,一一對(duì)應(yīng)。

第五個(gè)是將第四個(gè)參數(shù)的值一一對(duì)象的顯示(一一對(duì)應(yīng))在接下來的int形的id數(shù)組中窿撬,這個(gè)id數(shù)組就是Layout的xml文件中命名id形成的唯一的int型標(biāo)識(shí)符启昧。

SeekBar停止拖動(dòng)后的事件:

Java代碼

publicvoidonStopTrackingTouch(SeekBar?seekBar)?{//?停止拖動(dòng)

intprogress?=?seekBar.getProgress();

if(!autoChange?&&?manulChange)?{

intmusicMax?=?musicService.player.getDuration();//得到該首歌曲最長秒數(shù)

intseekBarMax?=?seekBar.getMax();

musicService.player

.seekTo(musicMax?*?progress?/?seekBarMax);//跳到該曲該秒

musicService.pause();

autoChange?=true;

manulChange?=false;

}

}

MusicHandler類的實(shí)現(xiàn):

Java代碼

classMusicHandlerextendsHandler?{

publicMusicHandler()?{

}

@Override

publicvoidhandleMessage(Message?msg)?{

if(autoChange)?{

try{

intposition?=?musicService.player.getCurrentPosition();//得到當(dāng)前歌曲播放進(jìn)度(秒)

intmMax?=?musicService.player.getDuration();//最大秒數(shù)

intsMax?=?seekBar.getMax();//seekBar最大值,算百分比

seekBar.setProgress(position?*?sMax?/?mMax);

txtInfo.setText(setPlayInfo(position?/1000,?mMax?/1000));

}catch(Exception?e)?{

e.printStackTrace();

}

}else{

seekBar.setProgress(0);

txtInfo.setText("播放已經(jīng)停止");

}

}

}

//設(shè)置當(dāng)前播放的信息

privateString?setPlayInfo(intposition,intmax)?{

String?info?="正在播放:??"+?musicService.songName?+"\t\t";

//笨辦法?寫完才想起可以用%的,但不想改了

intpMinutes?=0;

while(position?>=60)?{

pMinutes++;

position?-=60;

}

String?now?=?(pMinutes?<10?"0"+?pMinutes?:?pMinutes)?+":"

+?(position?<10?"0"+?position?:?position);

intmMinutes?=0;

while(max?>=60)?{

mMinutes++;

max?-=60;

}

String?all?=?(mMinutes?<10?"0"+?mMinutes?:?mMinutes)?+":"

+?(max?<10?"0"+?max?:?max);

returninfo?+?now?+"?/?"+?all;

}

MusicThread的實(shí)現(xiàn):

Java代碼

classMusicThreadimplementsRunnable?{

@Override

publicvoidrun()?{

while(true)

try{

musicHandler.sendMessage(newMessage());

Thread.sleep(1000);//?每間隔1秒發(fā)送一次更新消息

}catch(InterruptedException?e)?{

e.printStackTrace();

}

}

}

技術(shù)交流QQ群:364595326

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末劈伴,一起剝皮案震驚了整個(gè)濱河市密末,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌跛璧,老刑警劉巖严里,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異赡模,居然都是意外死亡田炭,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門漓柑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來教硫,“玉大人,你說我怎么就攤上這事辆布∷簿兀” “怎么了?”我有些...
    開封第一講書人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵锋玲,是天一觀的道長景用。 經(jīng)常有香客問我,道長惭蹂,這世上最難降的妖魔是什么伞插? 我笑而不...
    開封第一講書人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮盾碗,結(jié)果婚禮上媚污,老公的妹妹穿的比我還像新娘。我一直安慰自己廷雅,他們只是感情好耗美,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著航缀,像睡著了一般商架。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上芥玉,一...
    開封第一講書人閱讀 48,970評(píng)論 1 284
  • 那天蛇摸,我揣著相機(jī)與錄音,去河邊找鬼灿巧。 笑死赶袄,一個(gè)胖子當(dāng)著我的面吹牛诬烹,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播弃鸦,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼幢痘!你這毒婦竟也來了唬格?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤颜说,失蹤者是張志新(化名)和其女友劉穎购岗,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體门粪,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡喊积,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了玄妈。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片乾吻。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖拟蜻,靈堂內(nèi)的尸體忽然破棺而出绎签,到底是詐尸還是另有隱情,我是刑警寧澤酝锅,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布诡必,位于F島的核電站,受9級(jí)特大地震影響搔扁,放射性物質(zhì)發(fā)生泄漏爸舒。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一稿蹲、第九天 我趴在偏房一處隱蔽的房頂上張望扭勉。 院中可真熱鬧,春花似錦场绿、人聲如沸剖效。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽璧尸。三九已至,卻和暖如春熬拒,著一層夾襖步出監(jiān)牢的瞬間爷光,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來泰國打工澎粟, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蛀序,地道東北人欢瞪。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像徐裸,于是被迫代替她去往敵國和親遣鼓。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

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

  • 本人初學(xué)Android重贺,最近做了一個(gè)實(shí)現(xiàn)安卓簡單音樂播放功能的播放器骑祟,收獲不少,于是便記錄下來自己的思路與知識(shí)總結(jié)...
    落日柳風(fēng)閱讀 19,080評(píng)論 2 41
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,498評(píng)論 25 707
  • Android Handler機(jī)制系列文章整體內(nèi)容如下: Android Handler機(jī)制1之ThreadAnd...
    隔壁老李頭閱讀 8,152評(píng)論 8 57
  • 參加這個(gè)活動(dòng)气笙,仍然難以采取行動(dòng)次企。 看書,卻沒有心得潜圃。 如咸魚一般缸棵。 想起鄧教授的教導(dǎo),不要只是積累谭期,只是擁有堵第,要懂...
    runruan閱讀 76評(píng)論 0 0
  • 這世上真有人過著你想要的生活,既能朝九晚五崇堵,又能浪跡天涯型诚。浪跡天涯,所有看過金庸古龍小說的人都曾向往那樣的生活鸳劳,浪...
    木安m閱讀 1,313評(píng)論 27 15