一个简易的音频播放器实现
XML代码块
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:orientation="vertical" android:layout_height="match_parent">
<SeekBar android:id="@+id/seek_bar" android:max="100" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="10dp"/>
<LinearLayout android:layout_marginTop="5dp" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center">
<Button android:id="@+id/play_or_pause" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="播放"/>
<Button android:id="@+id/stop" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="停止"/>
</LinearLayout>
</LinearLayout>
界面图片
实现功能
- 可以通过拖动进度条来播放音乐,音乐播放时进度条也会跟着移动,当播放完毕时进度条走满
- 点击播放键进入播放状态,同时按键文字变为“暂停”,点击“暂停”时暂停播放,文字变为“播放”,下次再点击“播放”将继续从上次播放位置播放
- 在任意状态点击“停止”将会停止播放,下次播放从头开始
定义好功能接口,能控制播放功能以及UI界面改变功能。
public interface IPlayControl {
//播放状态
int PLAYER_STATE_PLAY=1;
int PLAYER_STATE_PAUSE=2;
int PLAYER_STATE_STOP=3;
void registerViewController(IPlayViewControl iPlayViewControl);
void unregisterViewController();
void playOrPause();
void stop();
void seekTo(float seek);
}
public interface IPlayViewControl {
void onPlayerStateChange(int state);
void onSeekChange(float seek);
}
在Activity中
@RequiresApi(api = Build.VERSION_CODES.M)
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private static final int PERMISSION_CODE = 1;
private SeekBar mSeekBar;
private Button mPlayOrPause;
private Button mStop;
private boolean mIsBind;
private IPlayControl mIPlayControl;
private boolean isUserTouchProgress;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//初始化控件
initView();
//设置事件监听
initListener();
//开启服务(因为绑定服务不能长期运行,所以采用二者结合的方式)
startService(new Intent(this, PlayService.class));
//因为要要在内存卡中读取音乐,需要获得运行时权限(模拟器api27,大于等于29的需要在
//AndroidManifest.xml中添加android:requestLegacyExternalStorage="true")
if ((checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED)) {
requestPermissions(new String[]{ Manifest.permission.READ_EXTERNAL_STORAGE},PERMISSION_CODE);
}else {
//绑定服务
initBindService();
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if(requestCode==PERMISSION_CODE){
if(grantResults[0]==PackageManager.PERMISSION_GRANTED){
//绑定服务
initBindService();
}
}
}
private void initBindService() {
Intent intent = new Intent(MainActivity.this, PlayService.class);
mIsBind = bindService(intent, mConnection, BIND_AUTO_CREATE);
Log.d(TAG, "bindService..");
}
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mIPlayControl = (IPlayControl) service;
mIPlayControl.registerViewController(mIPlayViewControl);
Log.d(TAG,"mConnection..");
}
@Override
public void onServiceDisconnected(ComponentName name) {
//如果断开连接就把它置空
mIPlayControl = null;
}
};
private void initListener() {
mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
//进度条发生改变
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
//用户用手拖动进度条
isUserTouchProgress=true;
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
//进度条停止拖动,获得当前进度
int touchProgress = seekBar.getProgress();
Log.d(TAG, "touchProgress==" + touchProgress);
if (mIPlayControl != null) {
//调用这个方法可以用进度条控制音频播放,传入的是一个百分比
mIPlayControl.seekTo(touchProgress);
}
isUserTouchProgress=false;
}
});
mPlayOrPause.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//播放或暂停
if (mIPlayControl != null) {
mIPlayControl.playOrPause();
}
}
});
mStop.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//关闭按钮被点击了
if (mIPlayControl != null) {
mIPlayControl.stop();
}
}
});
}
private void initView() {
mSeekBar = this.findViewById(R.id.seek_bar);
mPlayOrPause = this.findViewById(R.id.play_or_pause);
mStop = this.findViewById(R.id.stop);
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mIsBind && mConnection != null) {
Log.d(TAG, "onDestroy...");
//释放资源
mIPlayControl.unregisterViewController();
unbindService(mConnection);
mIsBind = false;
}
}
private IPlayViewControl mIPlayViewControl = new IPlayViewControl() {
@Override
public void onPlayerStateChange(int state) {
//跟据播放状态来更新UI
switch (state){
case PLAYER_STATE_PLAY:
//播放状态的话要修改按钮显示成暂停
mPlayOrPause.setText("暂停");
break;
case PLAYER_STATE_PAUSE:
case PLAYER_STATE_STOP:
//暂停状态的话要修改按钮显示成播放
mPlayOrPause.setText("播放");
break;
}
}
@Override
public void onSeekChange(final float seek) {
//改变进度条更新,有一个条件,当用户手碰在进度条上就不更新,不然会抖动
Log.d(TAG, "onSeekChange: -->"+seek);
runOnUiThread(new Runnable() {
@Override
public void run() {
if (!isUserTouchProgress) {
mSeekBar.setProgress((int)seek);
}
}
});
}
};
}
在Service中
public class PlayService extends Service {
private PlayerPresenter mPlayerPresenter;
@Override
public void onCreate() {
super.onCreate();
if(mPlayerPresenter==null){
//当mPlayerPresenter为空时创建,这是继承的Binder类
mPlayerPresenter=new PlayerPresenter();
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
//绑定之后返回一个IBinder类型
return mPlayerPresenter;
}
@Override
public void onDestroy() {
super.onDestroy();
//服务销毁时将其置空
mPlayerPresenter=null;
}
}
在PlayerPresenter中
继承了Binder类同时实现了IPlayContol接口,因为这个唯一能够使Service跟Activity进行数据交换的桥梁。
首先实现方法
public class PlayerPresenter extends Binder implements IPlayControl {
private static final String TAG = "PlayerPresenter";
//设置成成员变量控制UI
private IPlayViewControl mIPlayViewControl;
//设置初始播放状态为停止播放
private int currentState = PLAYER_STATE_STOP;
//定义一个播放器
public MediaPlayer mMediaPlayer;
//开启一个计时器
private Timer mTimer;
//开启一个任务
private SeekTimeTask mSeekTimeTaskask;
@Override
public void registerViewController(IPlayViewControl iPlayViewControl) {
//获得UI控制权
this.mIPlayViewControl = iPlayViewControl;
}
@Override
public void unregisterViewController() {
mIPlayViewControl=null;
}
@Override
public void playOrPause() {
Log.d(TAG,"playOrPause");
if(currentState==PLAYER_STATE_STOP){
//创建播放器
initPlayer();
//设置数据源
try {
//按照流程,具体可以参考Android.cn搜索MediaPlayer
mMediaPlayer.setDataSource("/mnt/sdcard/jay.mp3");
mMediaPlayer.prepare();
mMediaPlayer.start();
//改变当前状态
currentState=PLAYER_STATE_PLAY;
startTimer();
} catch (IOException e) {
e.printStackTrace();
}
}else if(currentState==PLAYER_STATE_PLAY){
//如果当前状态是播放的我们就暂停
if (mMediaPlayer != null) {
mMediaPlayer.pause();
currentState=PLAYER_STATE_PAUSE;
stopTimer();
}
}else if(currentState==PLAYER_STATE_PAUSE){
//如果当前是暂停的我们就继续播放音乐
if (mMediaPlayer != null) {
mMediaPlayer.start();
currentState=PLAYER_STATE_PLAY;
startTimer();
}
}
//通知UI界面
if (mIPlayViewControl != null) {
mIPlayViewControl.onPlayerStateChange(currentState);
}
}
private void initPlayer() {
if (mMediaPlayer==null) {
mMediaPlayer = new MediaPlayer();
//设置播放类型
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
}
}
@Override
public void stop() {
Log.d(TAG,"stop");
//stop的时候要判断是否有在播放
if (mMediaPlayer != null ) {
mMediaPlayer.stop();
//更新播放状态和UI
currentState=PLAYER_STATE_STOP;
stopTimer();
if (mIPlayViewControl != null) {
mIPlayViewControl.onPlayerStateChange(currentState);
}
//释放资源
mMediaPlayer.release();
mMediaPlayer=null;
}
}
@Override
public void seekTo(float seek) {
Log.d(TAG,"seekTo.."+seek);
//0~100之间,需要做一个转换,得到的seek是一个百分比
if (mMediaPlayer != null) {
int tarSeek = (int) (seek*1.0f/100 * mMediaPlayer.getDuration());
mMediaPlayer.seekTo(tarSeek);
}
}
private void startTimer(){
if (mTimer == null) {
mTimer = new Timer();
}
if (mSeekTimeTaskask == null) {
mSeekTimeTaskask = new SeekTimeTask();
}
//把任务扔到计时器里面,在特定的延迟时间每500ms更新一次
mTimer.schedule(mSeekTimeTaskask,0,500);
}
private void stopTimer(){
if (mTimer != null) {
mTimer.cancel();
mTimer=null;
}
if (mSeekTimeTaskask != null) {
mSeekTimeTaskask.cancel();
mSeekTimeTaskask=null;
}
}
//设定一个task放进Timer中,每隔一段时间进行一次
private class SeekTimeTask extends TimerTask {
@Override
public void run() {
if (mMediaPlayer != null && mIPlayViewControl!=null) {
//获取当前的播放进度
int currentPosition = mMediaPlayer.getCurrentPosition();
//Log.d(TAG, "current play position --->"+ currentPosition);
//换算成占100里面的多少了(因为设置的进度条数值为100)
float curPosition = currentPosition*1.0f / mMediaPlayer.getDuration()*100;
Log.d(TAG,"curPosition--->"+curPosition);
//设置进度条显示
mIPlayViewControl.onSeekChange(curPosition);
}
}
}
}