hunterliy小作品之 HunterMusic音乐播放器(Day2-后台播放服务实现)
来源:互联网 发布:数据库题库 编辑:程序博客网 时间:2024/04/30 01:56
1.1完成音乐播放布局
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/play_layout" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#e0e0e0" tools:context="com.example.administrator.huntermusic.activity.AudioPlayActivity"> <TextView android:id="@+id/music_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" android:layout_gravity="center" android:text="歌曲名" android:textSize="20sp" android:layout_marginTop="30dp"/> <TextView android:id="@+id/singer_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="歌手" android:layout_margin="5dp" android:layout_gravity="center" android:gravity="center"/> <com.example.administrator.huntermusic.utils.CDView android:id="@+id/CD_view" android:layout_marginBottom="80dp" android:layout_marginTop="50dp" android:layout_width="200dp" android:layout_height="200dp" android:layout_gravity="center"> </com.example.administrator.huntermusic.utils.CDView> <TextView android:id="@+id/tv_position" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="right" android:text="00:00/00:00" android:textSize="15sp" android:layout_margin="10dp" /> <SeekBar android:id="@+id/seekbar" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="6dp" android:layout_marginLeft="4dp" android:layout_marginRight="4dp" android:layout_margin="20dp" android:progress="0" android:thumbOffset="0dp" /> <LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="4dp" > <LinearLayout android:orientation="horizontal" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:gravity="center"> <ImageView android:id="@+id/play_mode" android:layout_width="32dp" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_margin="10dp" /> <ImageView android:id="@+id/pre" android:layout_width="32dp" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_margin="10dp" android:background="@drawable/pre"/> <ImageView android:id="@+id/play" android:layout_width="64dp" android:layout_height="wrap_content" android:layout_margin="10dp" /> <ImageView android:id="@+id/next" android:layout_width="32dp" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_margin="10dp" android:background="@drawable/next"/> <ImageView android:id="@+id/playlist" android:layout_width="32dp" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_margin="10dp" android:background="@drawable/playlist"/> </LinearLayout> </LinearLayout></LinearLayout>
其中的CDView可以先使用一个ImageView来代替,之后我们再慢慢完善。最终效果如下:
1.2播放音乐
播放音乐需要在服务中播放,因为在退出界面的时候,音乐还在播放,这导致音乐的不可控制。所以为了便于控制我们将播放的逻辑放在服务中。那么就需要将数据也传递到服务中。
AudioPlayActivity .java
package com.example.administrator.huntermusic.activity;import android.content.BroadcastReceiver;import android.content.ComponentName;import android.content.Context;import android.content.Intent;import android.content.IntentFilter;import android.content.ServiceConnection;import android.content.res.Resources;import android.support.v7.app.AppCompatActivity;import android.view.View;import android.view.Window;public class AudioPlayActivity extends AppCompatActivity implements View.OnClickListener{ private AudioServiceConnection audioServiceConnection; private AudioPlayService.AudioBinder binder; @Override protected void onCreate(Bundle savedInstanceState) { requestWindowFeature(Window.FEATURE_NO_TITLE); super.onCreate(savedInstanceState); setContentView(R.layout.activity_audio_play); initData(); initView(); } @Override protected void onDestroy(){ super.onDestroy(); unbindService(audioServiceConnection); } public void initData(){ Intent service = new Intent(getIntent()); service.setClass(this, AudioPlayService.class); audioServiceConnection = new AudioServiceConnection(); bindService(service, audioServiceConnection, BIND_AUTO_CREATE); startService(service); } public class AudioServiceConnection implements ServiceConnection { @Override public void onServiceConnected(ComponentName name, IBinder iBinder) { binder = (AudioPlayService.AudioBinder) iBinder; } @Override public void onServiceDisconnected(ComponentName name) { } }}
在服务中播放音乐
AudioPlayService .java
package com.example.administrator.huntermusic.service;import android.app.Service;import android.content.Intent;import android.media.MediaPlayer;import android.net.Uri;import android.os.Binder;import android.os.IBinder;import com.example.administrator.huntermusic.bean.AudioItem;import java.io.IOException;public class AudioPlayService extends Service { private AudioBinder binder; private List<AudioItem> audioItemList; private int position; @Override public IBinder onBind(Intent intent) { binder = new AudioBinder(); return binder; } @Override public void onCreate() { super.onCreate(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { audioItemList = (List<AudioItem>) intent.getSerializableExtra("audioItems"); position = intent.getIntExtra("position", -1); binder.play(); return super.onStartCommand(intent, flags, startId); } @Override public boolean onUnbind(Intent intent) { return super.onUnbind(intent); } @Override public void onDestroy() { super.onDestroy(); } public class AudioBinder extends Binder{ private MediaPlayer media_player; public void play(){ if (media_player!=null){ media_player.reset(); media_player.release(); media_player = null; } AudioItem audioItem = audioItemList.get(position); media_player = new MediaPlayer(); backIsPlay = true; try { media_player.setDataSource(AudioPlayService.this, Uri.parse(audioItem.getPath())); media_player.prepare(); media_player.setOnPreparedListener(new OnAudioPreparedListener(media_player.getCurrentPosition())); media_player.start(); } catch (IOException e) { e.printStackTrace(); } }}
1.3 完成界面功能
1.3.1在 AudioBinder 中提供音乐暂停开始的方法。在AudioPlayActivity 中点击开始按钮调用暂停开始的方法。
public void pause(){ media_player.pause(); backIsPlay = false; }public void start(){ media_player.start(); backIsPlay = true; }public boolean isPlaying(){ return media_player.isPlaying(); }
AudioPlayActivity 继承View .OnClickListener接口,在 AudioPlayActivity 中调用开始暂停播放的方法
@Override public void onClick(View v) { switch (v.getId()){ case R.id.play : switchPlayState(); break; } private void switchPlayState() { if (binder.isPlaying()){ binder.pause(); }else{ binder.start(); } updataPlayButton(); }
更新播放按钮
private void updataPlayButton(){ if (binder.isPlaying()) { play_button.setImageResource(R.drawable.pause_shadow); } else { play_button.setImageResource(R.drawable.play_shadow); } }
当音乐准备完成后需要改变界面按钮的状态为暂停按钮,并且初始化接收到的歌曲名和歌手名。在服务中发送广播,在界面中注册广播之后更新界面。
public class AudioBinder extends Binder{ private MediaPlayer media_player; public void play(){ if (media_player!=null){ media_player.reset(); media_player.release(); media_player = null; } AudioItem audioItem = audioItemList.get(position); media_player = new MediaPlayer(); backIsPlay = true; try { media_player.setDataSource(AudioPlayService.this, Uri.parse(audioItem.getPath())); media_player.prepare(); media_player.setOnPreparedListener(new OnAudioPreparedListener(media_player.getCurrentPosition())); media_player.start(); } catch (IOException e) { e.printStackTrace(); } public void pause(){ media_player.pause(); backIsPlay = false; }public void start(){ media_player.start(); backIsPlay = true; }public boolean isPlaying(){ return media_player.isPlaying(); }private class OnAudioPreparedListener implements MediaPlayer.OnPreparedListener{ @Override public void onPrepared(MediaPlayer mp) { mp.start(); Intent intent = new Intent("com.work.huntermusic.updateplaybtn"); //获取当前正在播放的音乐 AudioItem audioItem = audioItemList.get(position); intent.putExtra("audioItem",audioItem); sendBroadcast(intent); } }}
在 AudioPlayActivity 中注册广播,更新界面
IntentFilter intentFilter = new IntentFilter("com.work.huntermusic.updateplaybtn"); registerReceiver(new AudioBroadCastReceiver(), intentFilter);private class AudioBroadCastReceiver extends BroadcastReceiver{ @Override public void onReceive(Context context, Intent intent) { //更新界面的按钮 updataPlayButton(); AudioItem audioItem = (AudioItem) intent.getSerializableExtra("audioItem"); music_name.setText(audioItem.getName()); singer_name.setText(audioItem.getSinger()); }}
1.3.2时间进度更新
在服务中的 AudioBinder中提供获取播放总时长和当前播放进度的方法, 当 MediaPlayer准备完成后播放界面收到广播发消息开始更新
在 AudioBinder 中提供获取总时长和当前播放进度的方法
/** 获取音乐的播放总时长*/public int getDuration(){ return media_player.getDuration(); }/** 获取音乐当前播放进度*/public int getCurrentPosition(){ return media_player.getCurrentPosition(); }
在播放界面收到广播之后开始发消息更新
private class AudioBroadCastReceiver extends BroadcastReceiver{ @Override public void onReceive(Context context, Intent intent) { //更新界面的按钮 updataPlayButton(); AudioItem audioItem = (AudioItem) intent.getSerializableExtra("audioItem"); music_name.setText(audioItem.getName()); singer_name.setText(audioItem.getSinger()); //更新播放进度 updateCurrentPosition(); }} public void updataCurrentPosition(){ int duration=binder.getDuration(); int position=binder.getCurrentPosition(); seekbar.setMax(binder.getDuration()); String durationStr= StringUtil.formatDuration(duration); String positionStr=StringUtil.formatDuration(position); tv_position.setText(positionStr + " / " + durationStr); handler.sendEmptyMessageDelayed(UPDATA_POSITION,500); } private static final int UPDATA_POSITION = 0; private Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { switch (msg.what){ case UPDATA_POSITION: updataCurrentPosition(); break; } } };
这里给出StringUtil中时间转换的方法formatDuration()
public static String formatDuration(int duration){ int hour = 60*60*1000; int minute = 60*1000; int second = 1000; int ghour = duration/hour; int temp = duration%hour; int gminute = temp/minute; temp = temp%minute; int gsecond = temp/second; if (ghour==0){ return String.format("%02d:%02d",gminute,gsecond); }else{ return String.format("%02d:%02d:%2d",ghour,gminute,gsecond); } }
为了防止内存泄露,在界面销毁的时候也需要将消息都移除掉。
1.3.3时间进度条的实现
在播放页面收到广播之后给 SeekBar 进行设置最大进度,在更新播放进度的时候同时去更新当前进度并且滑动 SeekBar 的时候改变进度
设置当前播放进度
/** 更新播放进度*/ public void updateCurrentPosition() { int position=binder.getCurrentPosition(); //及时更新进度时间 updatePosition(position); handler.sendEmptyMessageDelayed(UPDATE_POSITION,500); }
滑动 SeekBar 的时候改变进度。在服务中提供改变进度的方法并且给 SeekBar 设置进度改变的监听
seekbar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { if (fromUser) { binder.seekTo(progress); updatePosition(progress); } } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onStopTrackingTouch(SeekBar seekBar) { binder.seekTo(seekBar.getProgress()); } });
服务 AudioBinder 中的改变进度的方法
/** 进度跳转*/ public void seekTo(int progress){ mediaPlayer.seekTo(progress); }
1.3.4播放上一首和下一首
在 AudioBinder 中提供播放上一首和下一首的方法,当点击按钮的时候调用播放上一首和下一首的方法。注意:在播放的时候需要将之前的 mediaplayer 进行重置
public void previous(){ if (position!=0){ position--; media_player.reset(); play(); }else { position = audioItemList.size()-1; } } public void next(){ if (position!=audioItemList.size()-1){ position++; media_player.reset(); play(); }else { position = 0; } }
然后在播放页面调用binder的方法前一首和后一首的方法
@Override public void onClick(View v) { switch (v.getId()){ case R.id.play : switchPlayState(); break; case R.id.pre : binder.previous(); break; case R.id.next : binder.next(); break; } }
1.3.5切换播放模式
在 AudioBinder 中提供切换播放模式的方法和获取当前播放模式
public static final int PLAY_ALL_REPEAT = 0; public static final int PLAY_RANDOM = 1; public static final int PLAY_SINGLE_REPEAT = 2; private int playMode = PLAY_ALL_REPEAT; public void changePlayMode(){ if (playMode == PLAY_ALL_REPEAT){ playMode = PLAY_RANDOM; }else if (playMode ==PLAY_RANDOM) { playMode = PLAY_SINGLE_REPEAT; }else if (playMode ==PLAY_SINGLE_REPEAT){ playMode = PLAY_ALL_REPEAT; } }
在播放页面点击切换播放模式的图片
@Override public void onClick(View v) { switch (v.getId()){ case R.id.play : switchPlayState(); break; case R.id.pre : binder.previous(); break; case R.id.next : binder.next(); break; case R.id.play_mode: binder.changePlayMode(); //及时更新播放模式 updataPlayMode(); break; } }private void updataPlayMode() { switch (binder.getPlayMode()){ case AudioPlayService.PLAY_ALL_REPEAT: play_mode.setImageResource(R.drawable.list_repeat); break; case AudioPlayService.PLAY_RANDOM: play_mode.setImageResource(R.drawable.random); break; case AudioPlayService.PLAY_SINGLE_REPEAT: play_mode.setImageResource(R.drawable.single_repeat); break; } }
1.3.6根据播放模式自动播放下一首音乐
在服务的 AudioBinder 的 play 方法中给 mediaplay 设置结束的监听,当音乐播放结束之后自动播放下一首
media_player.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) { //歌曲播放结束 //自动播放下一首 autoPlayNext(); } });private void autoPlayNext() { switch (playMode){ case PLAY_ALL_REPEAT: if (position == audioItemList.size()-1) { position = 0; }else { position++; } break; case PLAY_RANDOM: position = new Random().nextInt(audioItemList.size()); break; case PLAY_SINGLE_REPEAT: break; } play(); }
至此音乐播放服务和界面已经初步完成了,但是还有一些工作要作补充比如专辑图片和歌词等等,这个我会在之后进行补充,但是一个简略的MP3播放器已经成型,大家也可以根据我的代码自己加工构造出更好的作品,我只是作为抛砖引玉,同时希望也能得到大家的分享。
- hunterliy小作品之 HunterMusic音乐播放器(Day2-后台播放服务实现)
- hunterliy小作品之 HunterMusic音乐播放器(Day1-主页面实现)
- hunterliy小作品之 HunterMusic音乐播放器(Day3-自定义通知栏实现)
- hunterliy小作品之 HunterMusic音乐播放器(Day4-歌词显示实现)
- hunterliy小作品之 HunterMusic音乐播放器(开发介绍)
- ios实现音乐播放器后台播放
- 个人小作品之迷你音乐播放器(移动端)
- 小程序(之音乐播放器实现思路)
- 小程序(之音乐播放器源码实现)
- OS音频开发之音乐播放器 - 后台播放音乐
- Android-Service (基本知识,生命周期,实例-startService 启动的服务音乐播放器后台服务播放)
- Android-Service (基本知识,生命周期,实例-startService 启动的服务音乐播放器后台服务播放)
- Services实现的后台音乐播放器
- Android之Service服务实现音乐播放器
- Mediaplayer实现音乐播放器,支持后台播放
- IOS实现后台播放音乐
- Android实现音乐后台播放
- ionic 实现音乐后台播放
- 前端学习(20160927-1015)
- Julia: rand
- CentOS 7 Nginx 控制脚本
- Json与XML解析的区别比较
- #ifndef........#define........#endif
- hunterliy小作品之 HunterMusic音乐播放器(Day2-后台播放服务实现)
- 【codevs 4716】破损的键盘2
- 如何在XML设定android控件的颜色(十六进制颜色码)
- 谷歌电子市场第2天
- 自学-jq篇-ajax方法
- cgi错误集锦
- 本地存储密码的安全设计
- OGL选择和反馈模式
- 简化版线程池