android音樂播放器的開發實例

本文將引導大家做一個音樂播放器,在做這個Android開發實例的過程中,能夠幫助大家進一步熟悉和掌握學過的ListView和其他一些組件。爲了有更好的學習效果,其中很多功能我們手動實現,例如音樂播放的快進快退等。

先欣賞下本實例完成後運行的界面效果:


首先我們建立項目,我使用的SDK是Android2.2的,然後在XML中進行佈局。

上方是一個ListView用來顯示我們的音樂列表,中間是一個SeekBar可以拖動當前音樂的播放進度,之所以用SeekBar而不用ProgressBar是因爲我們需要音樂的快進快退功能,可以拖動滑桿改變進度;還有一個TextView,用來顯示當前播放歌曲的名字,時長等。最下方就是4個Button了,分別是上一曲,播放(暫停),停止,下一曲。

大家注意儘量不要在佈局中出現直接顯示在界面上的文字內容,我們把這些內容都放在res/values下的strings.xml中,然後分別引用它們,這樣養成良好的習慣,界面與內容分離,方便調試和後期維護等。現在我們的界面如下:


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


在mnt/sdcard下面,我們放個兩三首歌曲,在虛擬機中暫不支持中文,導入有中文的文件會報錯的。

接着我們創建一個類,做我們播放器的Service類,我就叫MusicService吧,在裏面聲明以下對象:

Java代碼

publicclassMusicService {

privatestaticfinalFile MUSIC_PATH = Environment

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

publicList musicList;// 存放找到的所有mp3的絕對路徑。

publicMediaPlayer player;// 定義多媒體對象

publicintsongNum;// 當前播放的歌曲在List中的下標

publicString songName;// 當前播放的歌曲名

}

然後我們去加載剛纔添加的MP3文件吧,這裏的方式多種多樣,我隨便寫一個簡單的了:

Java代碼

classMusicFilterimplementsFilenameFilter {

publicbooleanaccept(File dir, String name) {

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

}

}

在MusicService類的無參構造函數中實例化對象,並把這些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());

}

}

}

我們寫個方法,來設置當前播放歌曲的名字:(個人覺得這方法比較笨,但暫時沒想到別的辦法)

Java代碼

publicvoidsetPlayName(String dataSource) {

File file =newFile(dataSource);//假設爲D:\\mm.mp3

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

intindex = name.lastIndexOf(".");//找到最後一個.

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

}

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

我們分別使用聲明的多媒體對象的start、pause、stop等方法可以完成。

Java代碼

publicvoidstart() {

try{

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

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

setPlayName(dataSource);//截取歌名

player.setDataSource(dataSource);//爲多媒體對象設置播放路徑

player.prepare();//準備播放

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

//setOnCompletionListener 噹噹前多媒體對象播放完成時發生的事件

player.setOnCompletionListener(newOnCompletionListener() {

publicvoidonCompletion(MediaPlayer arg0) {

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

}

});

}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類就寫完了,接着我們去Activity中爲各控件綁定事件。

在這個Activity中,最難做的一點應該就是拖動SeekBar的滑桿改變播放進度了,這裏我考慮再三,用了一個Handler類來處理。

Handler在android裏負責發送和處理消息。它的主要用途有:

1.按計劃發送消息或執行某個Runnanble(使用POST方法)。

2.從其他線程中發送來的消息放入消息隊列中,避免線程衝突(常見於更新UI線程)。

默認情況下,Handler接受的是當前線程下的消息循環實例(使用Handler(Looper looper)、Handler(Looper looper, Handler.Callback callback)可以指定線程),同時一個消息隊列可以被當前線程中的多個對象進行分發、處理(在UI線程中,系統已經有一個Activity來處理了,你可以再起若干個Handler來處理)。在實例化Handler的時候,Looper可以是任意線程的,只要有Handler的指針,任何線程也都可以sendMessage。Handler對於Message的處理不是併發的。一個Looper 只有處理完一條Message纔會讀取下一條,所以消息的處理是阻塞形式的(handleMessage()方法裏不應該有耗時操作,可以將耗時操作放在其他線程執行,操作完後發送Message(通過sendMessges方法),然後由handleMessage()更新UI)。

聲明以下變量:

Java代碼

privateButton btnStart, btnStop, btnNext, btnLast;

privateTextView txtInfo;

privateListView listView;

privateSeekBar seekBar;

privateMusicService musicService;

privateMusicHandler musicHandler;// 處理改變進度條事件

privateMusicThread musicThread;// 自動改變進度條的線程

privatebooleanautoChange, manulChange;// 判斷是進度條是自動改變還是手動改變

privatebooleanisPause;// 判斷是從暫停中恢復還是重新播放

如有報錯的可以先註釋掉不用管它,然後在初始化過程中綁定事件。

這是ListView的填充方法:

Java代碼

privatevoidsetListViewAdapter() {

List<map> date =newArrayList<map>();</map</map

for(String path : musicService.musicList) {

Map map =newHashMap();</string, object></string, object>

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的構造函數是:

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

第一個參數context,是指在哪個Activity中顯示。

第二個參數是一個泛型作爲數據源,而且每一個List中的一行就代表着呈現出來的一行,Map的鍵就是這一行的列名,值也是有列名的。

第三個參數爲資源文件,就是說要加載這個列所需要的視圖資源文件,我直接引用系統內置的資源,如果你想要漂亮的樣式可以自己寫的。

第四個參數是一個String數組,主要是將Map對象中的名稱映射到列名,一一對應。

第五個是將第四個參數的值一一對象的顯示(一一對應)在接下來的int形的id數組中,這個id數組就是Layout的xml文件中命名id形成的唯一的int型標識符。

SeekBar停止拖動後的事件:

Java代碼

publicvoidonStopTrackingTouch(SeekBar seekBar) {// 停止拖動

intprogress = seekBar.getProgress();

if(!autoChange && manulChange) {

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

intseekBarMax = seekBar.getMax();

musicService.player

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

musicService.pause();

autoChange =true;

manulChange =false;

}

}

MusicHandler類的實現:

Java代碼

classMusicHandlerextendsHandler {

publicMusicHandler() {

}

@Override

publicvoidhandleMessage(Message msg) {

if(autoChange) {

try{

intposition = musicService.player.getCurrentPosition();//得到當前歌曲播放進度(秒)

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

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("播放已經停止");

}

}

}

//設置當前播放的信息

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的實現:

Java代碼

classMusicThreadimplementsRunnable {

@Override

publicvoidrun() {

while(true)

try{

musicHandler.sendMessage(newMessage());

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

}catch(InterruptedException e) {

e.printStackTrace();

}

}

}

技術交流QQ羣:364595326

發佈了10 篇原創文章 · 獲贊 6 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章