編輯:關於Android編程
音樂播放列表也准備好了,我們來播放音樂吧。完成後效果如下,
實現音樂的播放,我們需要播放界面和音樂服務兩個方面的合作。
前面我們已經為播放音樂的功能在MusicService中搭建好了框架,下面就要來實現它們了。這些實現接口的函數,我們在命名的時候都用上了
xxxInner()這樣的格式,例如
playInner(),這是功能真正實現的地方。
7.1.1 創建播放器
MediaPlayer是實現音樂播放的核心,所有與播放相關的操作都是通過它來完成的。當
MusicService運行起來的時候,就需要創建它的實例,退出時,要釋放
MediaPlayer實例,
public class MusicService extends Service {
......
private MediaPlayer mMusicPlayer;
@Override
public void onCreate() {
super.onCreate();
......
//創建
mMusicPlayer = new MediaPlayer();
......
}
@Override
public void onDestroy() {
super.onDestroy();
......
//釋放
mMusicPlayer.release();
}
......
}
7.1.2 監聽器
MusicService提供注冊監聽器的接口,當播放音樂的狀態發生變化的時候,能夠實時的將當前的狀態傳遞給關心的組件。
監聽器的接口在之前的章節中,我們已經定義成了,
public class MusicService extends Service {
public interface OnStateChangeListenr {
void onPlayProgressChange(MusicItem item);
void onPlay(MusicItem item);
void onPause(MusicItem item);
}
}
當某個組件(例如
MusicListActivity)想要監聽
MusicServcie的時候,就要利用
registerOnStateChangeListener()和
unregisterOnStateChangeListener()進行注冊。
MusicService把注冊的監聽器存儲到數組列表當中,需要的時候取出來使用,
public class MusicService extends Service {
//創建存儲監聽器的列表
private List mListenerList = new ArrayList();
private void registerOnStateChangeListenerInner(OnStateChangeListenr l) {
//將監聽器添加到列表
mListenerList.add(l);
}
private void unregisterOnStateChangeListenerInner(OnStateChangeListenr l) {
//將監聽器從列表中移除
mListenerList.remove(l);
}
@Override
public void onDestroy() {
super.onDestroy();
......
//當MusicService銷毀的時候,清空監聽器列表
mListenerList.clear();
}
}
7.1.3 控制播放
播放、暫停、播放前一首、播放後一首的功能實現,並通過監聽器通知監聽者
MusicService的狀態變化。當前要播放的歌曲,將被放在叫做
mCurrentMusicItem的
MusicItem數據結構中,
public class MusicService extends Service {
//當前是否為播放暫停狀態
private boolean mPaused;
//存放當前要播放的音樂
private MusicItem mCurrentMusicItem;
......
@Override
public void onCreate() {
super.onCreate();
......
mPaused = false;
......
}
......
//將要播放的音樂載入MediaPlayer,但是並不播放
private void prepareToPlay(MusicItem item) {
try {
//重置播放器狀態
mMusicPlayer.reset();
//設置播放音樂的地址
mMusicPlayer.setDataSource(MusicService.this, item.songUri);
//准備播放音樂
mMusicPlayer.prepare();
} catch (IOException e) {
e.printStackTrace();
}
}
//播放音樂,根據reload標志位判斷是非需要重新加載音樂
private void playMusicItem(MusicItem item, boolean reload) {
//如果這裡傳入的是空值,就什麼也不做
if(item == null) {
return;
}
if(reload) {
//需要重新加載音樂
prepareToPlay(item);
}
//開始播放,如果之前只是暫停播放,那麼音樂將繼續播放
mMusicPlayer.start();
//將音樂設置到指定時間開始播放,時間單位為毫秒
seekToInner((int)item.playedTime);
//將播放的狀態通過監聽器通知給監聽者
for(OnStateChangeListenr l : mListenerList) {
l.onPlay(item);
}
//設置為非暫停播放狀態
mPaused = false;
}
//播放播放列表中,當前音樂的下一首音樂
private void playNextInner() {
int currentIndex = mPlayList.indexOf(mCurrentMusicItem);
if(currentIndex < mPlayList.size() -1 ) {
//獲取當前播放(或者被加載)音樂的下一首音樂
//如果後面有要播放的音樂,把那首音樂設置成要播放的音樂
//並重新加載該音樂,開始播放
mCurrentMusicItem = mPlayList.get(currentIndex + 1);
playMusicItem(mCurrentMusicItem, true);
}
}
private void playInner() {
//如果之前沒有選定要播放的音樂,就選列表中的第一首音樂開始播放
if(mCurrentMusicItem == null && mPlayList.size() > 0) {
mCurrentMusicItem = mPlayList.get(0);
}
//如果是從暫停狀態恢復播放音樂,那麼不需要重新加載音樂;
//如果是從完全沒有播放過的狀態開始播放音樂,那麼就需要重新加載音樂
if(mPaused) {
playMusicItem(mCurrentMusicItem, false);
}
else {
playMusicItem(mCurrentMusicItem, true);
}
}
private void playPreInner() {
int currentIndex = mPlayList.indexOf(mCurrentMusicItem);
if(currentIndex - 1 >= 0 ) {
//獲取當前播放(或者被加載)音樂的上一首音樂
//如果前面有要播放的音樂,把那首音樂設置成要播放的音樂
//並重新加載該音樂,開始播放
mCurrentMusicItem = mPlayList.get(currentIndex - 1);
playMusicItem(mCurrentMusicItem, true);
}
}
private void pauseInner() {
//暫停當前正在播放的音樂
mMusicPlayer.pause();
//將播放狀態的改變通知給監聽者
for(OnStateChangeListenr l : mListenerList) {
l.onPause(mCurrentMusicItem);
}
//設置為暫停播放狀態
mPaused = true;
}
private void seekToInner(int pos) {
//將音樂拖動到指定的時間
mMusicPlayer.seekTo(pos);
}
private MusicItem getCurrentMusicInner() {
//返回當前正加載好的音樂
return mCurrentMusicItem;
}
private boolean isPlayingInner() {
//返回當前的播放器是非正在播放音樂
return mMusicPlayer.isPlaying();
}
......
}
7.1.4 通知播放進度
除了控制播放,還要能向監聽者報告當前音樂的播放進度。
我們設計的策略是:當音樂開始播放的時候,就每隔一秒鐘報告一次當前播放的進度。要實現每隔一秒報告一次,可以通過創建
Handler,每間隔一秒發送一條通知的消息來實現。
public class MusicService extends Service {
......
//定義循環發送的消息
private final int MSG_PROGRESS_UPDATE = 0;
//定義處理消息的Handler
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_PROGRESS_UPDATE: {
//將音樂的時長和當前播放的進度保存到MusicItem數據結構中,
mCurrentMusicItem.playedTime = mMusicPlayer.getCurrentPosition();
mCurrentMusicItem.duration = mMusicPlayer.getDuration();
//通知監聽者當前的播放進度
for(OnStateChangeListenr l : mListenerList) {
l.onPlayProgressChange(mCurrentMusicItem);
}
//將當前的播放進度保存到數據庫中
updateMusicItem(mCurrentMusicItem);
//間隔一秒發送一次更新播放進度的消息
sendEmptyMessageDelayed(MSG_PROGRESS_UPDATE, 1000);
}
break;
}
}
};
//將播放時間更新到ContentProvider中
private void updateMusicItem(MusicItem item) {
ContentValues cv = new ContentValues();
cv.put(DBHelper.DURATION, item.duration);
cv.put(DBHelper.LAST_PLAY_TIME, item.playedTime);
String strUri = item.songUri.toString();
mResolver.update(PlayListContentProvider.CONTENT_SONGS_URI, cv, DBHelper.SONG_URI + "=\"" + strUri + "\"", null);
}
......
}
當音樂開始播放的時候,觸發進度更新;當音樂暫停或者停止的時候,停止進度的更新;
public class MusicService extends Service {
......
private void playMusicItem(MusicItem item, boolean reload) {
......
//移除現有的更新消息,重新啟動更新
mHandler.removeMessages(MSG_PROGRESS_UPDATE);
mHandler.sendEmptyMessage(MSG_PROGRESS_UPDATE);
......
}
private void pauseInner() {
......
//停止更新
mHandler.removeMessages(MSG_PROGRESS_UPDATE);
......
}
@Override
public void onDestroy() {
super.onDestroy();
......
//停止更新
mHandler.removeMessages(MSG_PROGRESS_UPDATE);
......
}
}
7.1.5 連續播放
我們設計的播放規則是-將播放列表裡的音樂按照次序自動播放,直到播完。所以我們要在每一首音樂結束後,自動播放下一首音樂。為此,要給播放器注冊一個播放是否完成的監聽器,當監聽器被觸發,開始下一首音樂的播放,
public class MusicService extends Service {
......
@Override
public void onCreate() {
super.onCreate();
......
mMusicPlayer = new MediaPlayer();
mMusicPlayer.setOnCompletionListener(mOnCompletionListener);
......
}
private MediaPlayer.OnCompletionListener mOnCompletionListener = new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
//將當前播放的音樂記錄時間重置為0,更新到數據庫
//下次播放就可以從頭開始
mCurrentMusicItem.playedTime = 0;
updateMusicItem(mCurrentMusicItem);
//播放下一首音樂
playNextInner();
}
};
}
7.1.6 初始化待播音樂
當
MusicService啟動之後,要先讀取數據庫中存儲的播放列表,把第一首音樂作為默認的待播放的音樂,
public class MusicService extends Service {
private void initPlayList() {
......
if( mPlayList.size() > 0) {
mCurrentMusicItem = mPlayList.get(0);
}
}
}
7.1.7 添加到播放列表
關於我們定制的規則是這樣的:如果用戶選擇添加一首音樂,那麼添加後,自動播放該首音樂;如果用戶選擇添加一組音樂,那麼添加後,自動播放該組音樂列表中的第一首。所以添加播放列表的代碼還要做一些修改,
public class MusicService extends Service {
......
private void addPlayListInner(List items) {
mResolver.delete(PlayListContentProvider.CONTENT_SONGS_URI, null, null);
mPlayList.clear();
for (MusicItem item : items) {
//逐個添加到播放列表,不要求添加後播放
addPlayListInner(item, false);
}
//添加完成後,開始播放
mCurrentMusicItem = mPlayList.get(0);
playInner();
}
//將函數增加了needPlay參數
private void addPlayListInner(MusicItem item, boolean needPlay) {
if(mPlayList.contains(item)) {
return;
}
mPlayList.add(0, item);
insertMusicItemToContentProvider(item);
if(needPlay) {
//添加完成後,開始播放
mCurrentMusicItem = mPlayList.get(0);
playInner();
}
}
......
}
本文實例講述了Android控件之ScrollView用法。分享給大家供大家參考。具體如下:ScrollView滾動視圖是指當擁有很多內容,屏幕顯示不完時,需要通過滾動跳
微博開發遇到很多bug 總結一下 我遇到BUG (1) :sso package or singn error 出現這個問題 是我沒有在 博客中填寫正確的包
前言Android自定義控件經常會用到Canvas繪制2D圖形,在優化自己自定義控件技能之前,必須熟練掌握Canvas繪圖機制。本文從以下三個方面對Canvas繪圖機制進
Android-使用WebView顯示網頁WebView是安卓提供顯示web界面的工具類,可以像PC端的浏覽器那樣進行顯示:大致就是上圖那樣。其實也可以不用WebView