一个音乐播放需要数据
- 播放列表
- 是否在播放
- 播放模式
- 播放位置
- 当前歌曲播放进度
当列表改变时要通知界面列表改变,点击下一曲要通知界面进行下一曲数据显示。当然可以使用通知来进行,不过改变一个数据就要通知,是不是很麻烦。再说,音乐播放界面有几个,一个前台服务通知,一个主界面播放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 = 101;// 单曲循环
public static final int PLAYMODEL_LISTLOOP = 102;// 列表循环
public static final int PLAYMODEL_SHUFFLEPLAY = 103;// 随机播放
// 更新
public static final int COMMAND_UPDATE_PROGRESS = 104;// 更新进度条
public static final int COMMAND_UPDATE_VIEW = 105;// 更新界面
public static final int COMMAND_UPDATE_PLAY_PAUSE = 106;//播放——暂停
public static final int COMMAND_UPDATE_PLAYMODE = 107;//播放模式
public static final int COMMAND_UPDATE_SEEKTO = 108;//播放模式
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 = -1;
this.currentProgress = -1;
}
public void init(List<Song> songList, int position) {// 更换当前播放列表
this.songList = songList;
this.position = position;
this.isPlaying = false;
this.playMode = PLAYMODEL_LISTLOOP;
this.currentProgress = -1;
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()*1000);// 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不是很理想。
好了,要是大家还有更好的实现这类问题(数据改变即通知界面更新)想法,欢迎分享。