天天看點

音樂播放器--觀察者模式+單例

一個音樂播放需要資料

  • 播放清單
  • 是否在播放
  • 播放模式
  • 播放位置
  • 目前歌曲播放進度

當清單改變時要通知界面清單改變,點選下一曲要通知界面進行下一曲資料顯示。當然可以使用通知來進行,不過改變一個資料就要通知,是不是很麻煩。再說,音樂播放界面有幾個,一個前台服務通知,一個主界面播放fragment,一個歌曲播放的fragment相當于有3個界面要更新,當然這些界面不一定是全在前台。這裡不使用通知,完成不同界面之間的資料傳遞以及fragment與service之間的資料傳遞(感覺就是使用靜态變量完成的)

觀察者模式大家應該比較熟悉,listview,adapter,java也實作了,在util包内有observer.其中有觀察者和被觀察者,最終完成效果如下

音樂播放器--觀察者模式+單例
//被觀察者要實作的接口
/**
 * @author xiaozhu
 * @date 2015-8-13 下午8:15:35
 */
public interface Observable {
    public void addObserver(Observer observer);// 添加觀察者
    public void removeObserver(Observer observer);// 移除觀察者
    public void notifyAllObserver(int command);// 通知所有觀察者
}
           

>

public class MusicData implements Observable {
    private List<Observer> userList = new ArrayList<Observer>();// 記錄需要更新的界面
    private static final MusicData musicData = new MusicData();// 單例模式

    public static MusicData getInstance() {
        return musicData;
    }

    // 播放模式
    public static final int PLAYMODEL_SINGLELOOP = ;// 單曲循環
    public static final int PLAYMODEL_LISTLOOP = ;// 清單循環
    public static final int PLAYMODEL_SHUFFLEPLAY = ;// 随機播放
    // 更新
    public static final int COMMAND_UPDATE_PROGRESS = ;// 更新進度條
    public static final int COMMAND_UPDATE_VIEW = ;// 更新界面
    public static final int COMMAND_UPDATE_PLAY_PAUSE = ;//播放——暫停
    public static final int COMMAND_UPDATE_PLAYMODE = ;//播放模式
    public static final int COMMAND_UPDATE_SEEKTO = ;//播放模式

    private List<Song> songList;// 播放清單
    private boolean isPlaying;// 是否在播放
    private int playMode;// 播放模式
    private int position;// 播放位置
    private int currentProgress;// 目前歌曲播放進度


    public MusicData() {
        this.songList = new ArrayList<Song>();
        this.isPlaying = false;
        this.playMode = PLAYMODEL_LISTLOOP;
        this.position = -;
        this.currentProgress = -;
    }

    public void init(List<Song> songList, int position) {// 更換目前播放清單
        this.songList = songList;
        this.position = position;
        this.isPlaying = false;
        this.playMode = PLAYMODEL_LISTLOOP;
        this.currentProgress = -;
        notifyAllObserver(COMMAND_UPDATE_VIEW);
    }

    @Override
    public void addObserver(Observer observer) {
        userList.add(observer);
    }

    @Override
    public void removeObserver(Observer observer) {
        userList.remove(observer);
    }

    @Override
    public void notifyAllObserver(int command) {
        for (Observer observer : userList) {
            observer.update(this, command);
        }
    }
    ...
}
           
被觀察者使用單例模式,保證目前播放所需資料隻有一個
//觀察者要實作的接口
/**
 * @author xiaozhu
 * @date 2015-8-13 下午8:15:35
 */
public interface Observer {
    public void update(Observable observable,int command);
}
           
為什麼要在update()裡面加個command,因為不是所有情況都一樣,或者每次都是界面全部更新。比如播放、暫停你需要通知界面更改播放圖示。
public class MusicPlayService extends Service implements Observer {
    @Override
    public void update(Observable observable, int command) {
        switch (command) {
        case MusicData.COMMAND_UPDATE_VIEW:
            song = musicData.getSong();
            if (mPlayer.isPlaying()) {
                mPlayer.pause();
            }
            mPlayer.reset();
            try {
                if (!TextUtils.isEmpty(song.getLocallink())) {
                    mPlayer.setDataSource(song.getLocallink());
                } else if (!TextUtils.isEmpty(song.getSonglink())) {
                    mPlayer.setDataSource(song.getSonglink());
                } else {
                    return;
                }
                mPlayer.prepare();
                if (musicData.isPlaying()) {
                    mPlayer.start();
                }
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (SecurityException e) {
                e.printStackTrace();
            } catch (IllegalStateException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            if(musicData.isPlaying()){
                mPlayer.start();
            }
            break;
//      case MusicData.COMMAND_UPDATE_PROGRESS:         
//          break;
        case MusicData.COMMAND_UPDATE_SEEKTO:

            mPlayer.seekTo(musicData.getCurrentProgress()*);// seekbar設定目前播放進度

            break;
        case MusicData.COMMAND_UPDATE_PLAY_PAUSE:

            if(musicData.isPlaying()){
                mPlayer.start();
            }else{
                mPlayer.pause();
            }

            break;
        case MusicData.COMMAND_UPDATE_PLAYMODE:
            break;
        default:
            break;
        }
    }
}
           
這裡隻貼出部分代碼
case MusicData.COMMAND_UPDATE_PROGRESS:         
        break;
case MusicData.COMMAND_UPDATE_SEEKTO:
        break;
           

這裡對于進度條有一個PROGRESS和一個SEEKTO,

在我們音樂播放時進度條每秒都在更新,使用COMMAND_UPDATE_PROGRESS通知界面進行進度條更新,使用COMMAND_UPDATE_SEEKTO進行點選拖動進度條播放

再來看看playcontrol的update方法
@Override
    public void update(Observable observable, int command) {
        switch (command) {
        case MusicData.COMMAND_UPDATE_VIEW:
            updateView();
            break;
        case MusicData.COMMAND_UPDATE_PROGRESS:
            tv_current.setText("" + musicData.getCurrentProgress());
            sb_musicControl.setProgress(musicData.getCurrentProgress());// seekbar設定目前播放進度
            break;
        // case MusicData.COMMAND_UPDATE_SEEKTO:
        // sb_musicControl.setProgress(musicData.getCurrentProgress());//
        // seekbar設定目前播放進度
        // break;
        case MusicData.COMMAND_UPDATE_PLAY_PAUSE:
            if (musicData.isPlaying()) {
                ib_play.setImageResource(R.drawable.playcontrol_ib_pause_default);
            } else {
                ib_play.setImageResource(R.drawable.playcontrol_ib_play_default);
            }
            break;
        case MusicData.COMMAND_UPDATE_PLAYMODE:
            updateModeView();
        }
    }
           

很簡單,就是界面的更新

不想說明了,自己看代碼吧,下載下傳位址:

本音樂播放器http://download.csdn.net/detail/zjabc520/9016783

這個是沒有寫完的,裡面有些不合理位置,使用xutil,界面也很醜并且沒有優化,是以前寫的直接copy的,也沒有用注解,主要是想說明完成的想法:觀察者模式+單例。因為以前完成這個播放子產品,我用了綁定服務,也用過通知,但是效果都不好。

這裡面也實作了點選listview更改背景色和文字顔色,裡面的adapter這樣寫可以解決點選一項清單滑動時後面背景顔色也變的問題。

還有百度音樂的排行榜API,網上查的,不過這個API不是很理想。

好了,要是大家還有更好的實作這類問題(資料改變即通知界面更新)想法,歡迎分享。

繼續閱讀