天天看点

音乐播放器--观察者模式+单例

一个音乐播放需要数据

  • 播放列表
  • 是否在播放
  • 播放模式
  • 播放位置
  • 当前歌曲播放进度

当列表改变时要通知界面列表改变,点击下一曲要通知界面进行下一曲数据显示。当然可以使用通知来进行,不过改变一个数据就要通知,是不是很麻烦。再说,音乐播放界面有几个,一个前台服务通知,一个主界面播放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不是很理想。

好了,要是大家还有更好的实现这类问题(数据改变即通知界面更新)想法,欢迎分享。

继续阅读