前一篇已经将视频模块弄好了,今天主要理一下音乐这一模块,包括:
下面是具体获取的方法(跟获取视频一模一样)。
private AudioListAdapter adapter;
private SimpleQueryHandler queryHandler;
@Override
protected void initData() {
adapter = new AudioListAdapter(getActivity(), null, false);
lv.setAdapter(adapter);
queryHandler = new SimpleQueryHandler(getActivity().getContentResolver());
String[] projection = {Media._ID, Media.DISPLAY_NAME, Media.DURATION,
Media.DATA, Media.ARTIST};
queryHandler.startQuery(0, adapter, Media.EXTERNAL_CONTENT_URI,
projection, null, null, null);
}
class SimpleQueryHandler extends AsyncQueryHandler {
public SimpleQueryHandler(ContentResolver cr) {
super(cr);
}
/** * token: 查询的标识 */
@Override
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
super.onQueryComplete(token, cookie, cursor);
if (cookie != null && cookie instanceof CursorAdapter) {
CursorAdapter adapter = (CursorAdapter) cookie;
adapter.changeCursor(cursor);//相当于notifyDatasetChange
CursorUtil.printCursor(cursor);
}
}
}
至此,可以实现上面音乐的列表。
音乐之所以能播放并且能切换上/下一首,肯定在点击listview的条目的时候把所有数据都传递过来了。
lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Cursor cursor = adapter.getCursor();
Bundle bundle = new Bundle();
bundle.putInt("currentPosition", position);
bundle.putSerializable("audioList", cursorToList(cursor));
enterActivity(AudioPlayerActivity.class, bundle);
}
});
private ArrayList<AudioItem> cursorToList(Cursor cursor){
cursor.moveToPosition(-1);
ArrayList<AudioItem> list = new ArrayList<AudioItem>();
while (cursor.moveToNext()){
AudioItem audioItem = AudioItem.fromCursor(cursor);
list.add(audioItem);
}
return list;
}
播放页面布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@mipmap/base_bg" android:orientation="vertical">
<!--titleBar-->
<RelativeLayout android:layout_width="match_parent" android:layout_height="50dp" android:background="@mipmap/base_titlebar_bg" android:orientation="horizontal">
<ImageView android:id="@+id/iv_back" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/selector_btn_back" />
<TextView android:id="@+id/tv_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:textColor="@color/white" android:textSize="20sp" />
</RelativeLayout>
<!--音频跳动的动画和歌曲艺术家-->
<RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content">
<ImageView android:id="@+id/iv_anim" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:background="@drawable/audio_player_anim" />
<TextView android:id="@+id/tv_artist" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignBottom="@id/iv_anim" android:layout_centerHorizontal="true" android:textColor="@color/white" android:textSize="16sp" />
</RelativeLayout>
<!--自定义歌词-->
<com.justforme.mobileplayer.ui.view.LyricView android:id="@+id/lyric_view" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" />
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="10dp">
<!--歌曲进度条和歌曲时间-->
<TextView android:id="@+id/tv_time" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="right" android:textColor="@color/white" android:textSize="16sp" />
<SeekBar android:id="@+id/sb_audio" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="3dp" android:progressDrawable="@drawable/audio_progress_drawable" android:thumb="@mipmap/audio_seek_thumb" android:thumbOffset="0dp" />
<!--各种播放逻辑的控件布局-->
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="5dp" android:layout_marginTop="5dp" android:gravity="center" android:orientation="horizontal">
<RelativeLayout android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1">
<ImageView android:id="@+id/iv_play_mode" android:layout_width="40dp" android:layout_height="40dp" android:layout_centerInParent="true" android:background="@drawable/selector_btn_mode_order" />
</RelativeLayout>
<RelativeLayout android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1">
<ImageView android:id="@+id/iv_pre" android:layout_width="45dp" android:layout_height="45dp" android:layout_centerInParent="true" android:background="@drawable/selector_btn_audio_pre" />
</RelativeLayout>
<RelativeLayout android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1">
<ImageView android:id="@+id/iv_play" android:layout_width="40dp" android:layout_height="40dp" android:layout_centerInParent="true" android:background="@drawable/selector_btn_audio_pause" />
</RelativeLayout>
<RelativeLayout android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1">
<ImageView android:id="@+id/iv_next" android:layout_width="45dp" android:layout_height="45dp" android:layout_centerInParent="true" android:background="@drawable/selector_btn_audio_next" />
</RelativeLayout>
<RelativeLayout android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1">
<ImageView android:layout_width="40dp" android:layout_height="40dp" android:layout_centerInParent="true" android:background="@drawable/selector_btn_lyric" />
</RelativeLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>
音频跳动的动画效果 (准备9张图片)
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false">
<item android:drawable="@mipmap/now_playing_matrix_01" android:duration="200" />
<item android:drawable="@mipmap/now_playing_matrix_02" android:duration="200" />
<item android:drawable="@mipmap/now_playing_matrix_03" android:duration="200" />
<item android:drawable="@mipmap/now_playing_matrix_04" android:duration="200" />
<item android:drawable="@mipmap/now_playing_matrix_05" android:duration="200" />
<item android:drawable="@mipmap/now_playing_matrix_06" android:duration="200" />
<item android:drawable="@mipmap/now_playing_matrix_07" android:duration="200" />
<item android:drawable="@mipmap/now_playing_matrix_08" android:duration="200" />
<item android:drawable="@mipmap/now_playing_matrix_09" android:duration="200" />
</animation-list>
//播放页面控件加载完成后,加载音频跳动动画
AnimationDrawable animationDrawable = (AnimationDrawable) ivAnim.getBackground();
animationDrawable.start();
可以再看下上面的结构图,播放音乐等相关功能是在服务中实现的,通过服务的中间人对象,实现交互。
下面这些方法是写在服务中间人对象(AudioServiceBinder)中的。
播放音乐
/** * 播放音乐 */
public void playAudio() {
if (mediaPlayer != null) {
mediaPlayer.release();
mediaPlayer = null;
}
mediaPlayer = new MediaPlayer();
AudioItem audioItem = audioList.get(currentPosition);
try {
mediaPlayer.setDataSource(AudioPlayerService.this, Uri.parse(audioItem.getPath()));
mediaPlayer.setOnPreparedListener(onPreparedListener);
mediaPlayer.setOnCompletionListener(onCompletionListener);
mediaPlayer.prepareAsync();
} catch (IOException e) {
e.printStackTrace();
}
}
判断音乐是否正在播放
public boolean isPlaying() {
return mediaPlayer != null && mediaPlayer.isPlaying();
}
暂停
public void pause() {
if (mediaPlayer != null) {
mediaPlayer.pause();
}
}
播放
public void start() {
if (mediaPlayer != null) {
mediaPlayer.start();
}
}
获取歌曲总时间
public long getDuration() {
return mediaPlayer == null ? 0 : mediaPlayer.getDuration();
}
获取歌曲当前的时间
public long getCurrentPosition() {
return mediaPlayer == null ? 0 : mediaPlayer.getCurrentPosition();
}
拖动歌曲到指定的时间
public void seekTo(int progress) {
if (mediaPlayer != null) {
mediaPlayer.seekTo(progress);
}
}
播放上一首
public void playPre() {
if (currentPosition > 0) {
currentPosition--;
playAudio();
}
}
播放下一首
public void playNext() {
if (currentPosition < audioList.size() - 1) {
currentPosition++;
playAudio();
}
}
获取当前正在播放的音乐的position
public int getCurrentPlayingIndex() {
return currentPosition;
}
获取音乐集合的大小
public int getAudioListSize() {
return audioList.size();
}
切换播放模式
public void switchPlayMode() {
switch (playMode) {
case MODE_ORDER://顺序播放 --> 单曲循环
playMode = MODE_SINGLE_REPEAT;
break;
case MODE_SINGLE_REPEAT://单曲循环 --> 循环播放
playMode = MODE_ALL_REPEAT;
break;
case MODE_ALL_REPEAT://循环播放 --> 顺序播放
playMode = MODE_ORDER;
break;
}
savePlayMode();
}
保存播放模式
private void savePlayMode() {
SharedPreferenceUtils.putInt(AudioPlayerService.this, "playMode", playMode);
}
获取播放模式(让外界获取)
public int getPlayMode() {
return playMode;
}
从SharedPreferences中获取播放模式
private int getPlayModeFromSp() {
return SharedPreferenceUtils.getInt(AudioPlayerService.this, "playMode", MODE_ORDER);
}
歌曲准备完成的监听器
private OnPreparedListener onPreparedListener = new OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
mediaPlayer.start();
notifyPrepared();
}
};
/** * 音乐准备完成时,发送广播将数据传给外界 */
private void notifyPrepared() {
Intent intent = new Intent(ACTION_PREPARED);
intent.putExtra("audioItem", audioList.get(currentPosition));
sendBroadcast(intent);
}
歌曲播放完成的监听器
private OnCompletionListener onCompletionListener = new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
notifyCompletion();
autoPlayByPlayMode();
}
};
private void notifyCompletion() {
Intent intent = new Intent(ACTION_COMPLETION);
intent.putExtra("audioItem", audioList.get(currentPosition));
sendBroadcast(intent);
}
根据播放模式自动播放
private void autoPlayByPlayMode() {
switch (playMode) {
case MODE_ORDER:
serviceBinder.playNext();
break;
case MODE_SINGLE_REPEAT:
serviceBinder.playAudio();
break;
case MODE_ALL_REPEAT:
if (currentPosition == audioList.size() - 1) {
currentPosition = 0;
serviceBinder.playAudio();
} else {
serviceBinder.playNext();
}
break;
}
}
至此,service中的方法基本上写好了。下面音乐播放的activity直接调用这些方法就可以了。
养成好习惯,服务销毁的时候,释放资源。
@Override
public void onDestroy() {
super.onDestroy();
mediaPlayer.release();
mediaPlayer = null;
}
初始化数据
@Override
protected void initData() {
registerAudioStateReceiver();
Bundle bundle = new Bundle();
//将音乐列表传递过来的数据(所有音乐的集合和当期音乐的position)传递给service
bundle = getIntent().getExtras();
Intent serviceIntent = new Intent(this, AudioPlayerService.class);
serviceIntent.putExtras(bundle);
bindService(serviceIntent, new AudioPlayerServiceConn(), Service.BIND_AUTO_CREATE);//为了拿到serviceBinder对象
startService(serviceIntent);//为了传递数据
}
/** * 注册一个音乐状态的广播接受者 */
private void registerAudioStateReceiver() {
IntentFilter filter = new IntentFilter();
filter.addAction(AudioPlayerService.ACTION_PREPARED);
filter.addAction(AudioPlayerService.ACTION_COMPLETION);
audioServiceReceiver = new AudioServiceReceiver();
registerReceiver(audioServiceReceiver, filter);
}
private class AudioServiceReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(AudioPlayerService.ACTION_PREPARED)) { //音乐准备完成
AudioItem audioItem = (AudioItem) intent.getSerializableExtra("audioItem");
tvTitle.setText(StringUtil.formatAudioName(audioItem.getTitle()));
tvArtist.setText(audioItem.getArtist());
tvTime.setText("00:00/" + StringUtil.formatVideoDuration(audioItem.getDuration()));
sbAudio.setMax((int) audioItem.getDuration());
updatePlayProgress();
updatePlayModeBtnBg(false);
} else if (action.equals(AudioPlayerService.ACTION_COMPLETION)) { //音乐播放完成
handler.removeMessages(UPDATE_PROGRESS);
AudioItem audioItem = (AudioItem) intent.getSerializableExtra("audioItem");
tvTime.setText(StringUtil.formatVideoDuration(audioItem.getDuration()) +
"/" + StringUtil.formatVideoDuration(audioItem.getDuration()));
sbAudio.setProgress((int) audioItem.getDuration());
ivPlay.setBackgroundResource(R.drawable.selector_btn_audio_play);
}
}
}
/** * 更新播放进度 */
private void updatePlayProgress() {
long currentProgress = serviceBinder.getCurrentPosition();
tvTime.setText(StringUtil.formatVideoDuration(currentProgress) + "/" + StringUtil.formatVideoDuration(serviceBinder.getDuration()));
sbAudio.setProgress((int) currentProgress);
handler.sendEmptyMessageDelayed(UPDATE_PROGRESS, 1000);
}
控件的点击事件
@Override
protected void processClick(View view) {
switch (view.getId()) {
case R.id.iv_back:
finish();
break;
case R.id.iv_play_mode:
serviceBinder.switchPlayMode();
updatePlayModeBtnBg(true);
break;
case R.id.iv_pre:
if (serviceBinder.getCurrentPlayingIndex() != 0) {
serviceBinder.playPre();
} else {
ToastUtil.showShort(this, "已经是第一首了");
}
break;
case R.id.iv_play:
if (serviceBinder.isPlaying()) {
serviceBinder.pause();
handler.removeMessages(UPDATE_PROGRESS);
handler.removeMessages(UPDATE_LYRIC);
} else {
serviceBinder.start();
handler.sendEmptyMessage(UPDATE_PROGRESS);
handler.sendEmptyMessage(UPDATE_LYRIC);
}
updatePlayBtnBg();
break;
case R.id.iv_next:
if (serviceBinder.getCurrentPlayingIndex() != serviceBinder.getAudioListSize() - 1) {
serviceBinder.playNext();
} else {
ToastUtil.showShort(this, "已经是最后一首了");
}
break;
}
}
/**更新播放模式按钮的背景 * @param isShowTip 是否显示建议(切换时的toast提醒) */
private void updatePlayModeBtnBg(boolean isShowTip) {
int playMode = serviceBinder.getPlayMode();
switch (playMode) {
case AudioPlayerService.MODE_ORDER:
if (isShowTip) ToastUtil.showShort(this, "顺序播放");
ivPlayMode.setBackgroundResource(R.drawable.selector_btn_mode_order);
break;
case AudioPlayerService.MODE_SINGLE_REPEAT:
if (isShowTip) ToastUtil.showShort(this, "单曲循环");
ivPlayMode.setBackgroundResource(R.drawable.selector_btn_mode_single_repeat);
break;
case AudioPlayerService.MODE_ALL_REPEAT:
if (isShowTip) ToastUtil.showShort(this, "循环播放");
ivPlayMode.setBackgroundResource(R.drawable.selector_btn_mode_all_repeat);
break;
}
}
/** * 更新播放按钮的背景 */
private void updatePlayBtnBg() {
ivPlay.setBackgroundResource(serviceBinder.isPlaying() ?
R.drawable.selector_btn_audio_pause :
R.drawable.selector_btn_audio_play);
}
基本功能完成了,记住在activity销毁的时候,移除一些消息和广播。
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(audioServiceReceiver);
handler.removeCallbacksAndMessages(null);
}
好了,先整理到这里。下一篇将整理音乐发送系统通知栏的相关知识,以及滚动歌词的绘制