【达内课程】音乐播放器2.0(下)

经过前两节我们已经把音乐播放器2.0改造成了使用 Service 播放音乐,当我们回到桌面也是可以继续播放的。

接下来我们可以再添加一个退出时的对话框提醒。

首先声明 AlertDialog

		private AlertDialog alertDialog;

在onCreate中创建,其中传入 this 需要让 Activity 实现 DialogInterface.OnClickListener,后边会写。

		alertDialog = new AlertDialog.Builder(this)
                .setTitle("提示")
                .setMessage("你确定要退出程序吗")
                .setPositiveButton("确定", this)
                .setNegativeButton("取消", this)
                .setNeutralButton("后台播放", this)
                .create();

按下返回键的时候显示

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            alertDialog.show();
            //返回
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }

给确定和取消按钮增加点击事件,让 Activity 实现 DialogInterface.OnClickListener,并实现这个接口的方法。注意区分View.OnClickListenerDialogInterface.OnClickListener

/**
     * @param dialog 点击的哪一个Dialog
     * @param which  点击的哪一个按钮
     */
    @Override
    public void onClick(DialogInterface dialog, int which) {
        switch (which) {
            case DialogInterface.BUTTON_POSITIVE:
                finish();
                break;
            case DialogInterface.BUTTON_NEGATIVE:
                alertDialog.dismiss();
                break;
            case DialogInterface.BUTTON_NEUTRAL:
                Intent intent = new Intent();
                intent.setAction(Intent.ACTION_MAIN);
                intent.addCategory(Intent.CATEGORY_HOME);
                startActivity(intent);
                break;
        }
    }

运行程序,分别点击 取消、后台播放和确定按钮
在这里插入图片描述
最后附上完整的 MainActivity 和 PlayMusicService 代码

MainActivity.java

public class MainActivity extends AppCompatActivity implements DialogInterface.OnClickListener {
    private List<Music> musics;
    private ListView listView;
    private MusicAdapter adapter;

    private TextView tvCurrentMusicTitle;
    private ImageButton ibPlay;//播放/暂停按钮
    private ImageButton ibPrevious;//上一首按钮
    private ImageButton ibNext;//下一首按钮

    private SeekBar seekBar;
    private TextView tvCurrentTime;
    private TextView tvDuration;
    //是否正在拖拽进度条
    private boolean isTrackingTouch;

    private AlertDialog alertDialog;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //绑定Service
        Intent intent = new Intent(this, PlayMusicService.class);
        conn = new InnerServiceConnection();
        bindService(intent, conn, BIND_AUTO_CREATE);

        //获取数据
        IDao<Music> dao = MusicDaoFactory.newInstance();
        musics = dao.getData();
        //初始化控件
        initView();
        //添加点击事件
        initListener();
        //创建退出时的对话框
        createDialog();
    }

    private void initView() {
        listView = findViewById(R.id.listview);
        adapter = new MusicAdapter(this, musics);
        listView.setAdapter(adapter);

        tvCurrentMusicTitle = findViewById(R.id.tv_current_music_title);
        ibPlay = findViewById(R.id.ib_play);
        ibPrevious = findViewById(R.id.ib_previous);
        ibNext = findViewById(R.id.ib_next);

        seekBar = findViewById(R.id.sk_music);
        tvCurrentTime = findViewById(R.id.tv_music_current_time);
        tvDuration = findViewById(R.id.tv_music_duration);
    }

    private void initListener() {
        InnerClassOnClickListener listener = new InnerClassOnClickListener();
        ibPlay.setOnClickListener(listener);
        ibPrevious.setOnClickListener(listener);
        ibNext.setOnClickListener(listener);

        //为listview添加点击事件
        OnItemClickListener onItemClickListener = new OnItemClickListener();
        listView.setOnItemClickListener(onItemClickListener);

        //为Seekbar添加监听
        OnSeekBarChangeListener onSeekBarChangeListener = new OnSeekBarChangeListener();
        seekBar.setOnSeekBarChangeListener(onSeekBarChangeListener);
    }

    private void createDialog() {
        alertDialog = new AlertDialog.Builder(this)
                .setTitle("提示")
                .setMessage("你确定要退出程序吗")
                .setPositiveButton("确定", this)
                .setNegativeButton("取消", this)
                .setNeutralButton("后台播放", this)
                .create();
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            alertDialog.show();
            //返回
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }

    /**
     * @param dialog 点击的哪一个Dialog
     * @param which  点击的哪一个按钮
     */
    @Override
    public void onClick(DialogInterface dialog, int which) {
        switch (which) {
            case DialogInterface.BUTTON_POSITIVE:
                finish();
                break;
            case DialogInterface.BUTTON_NEGATIVE:
                alertDialog.dismiss();
                break;
            case DialogInterface.BUTTON_NEUTRAL:
                Intent intent = new Intent();
                intent.setAction(Intent.ACTION_MAIN);
                intent.addCategory(Intent.CATEGORY_HOME);
                startActivity(intent);
                break;
        }
    }

    //按钮监听
    private class InnerClassOnClickListener implements View.OnClickListener {
        @Override
        public void onClick(View view) {
            switch (view.getId()) {
                case R.id.ib_play:
                    if (player.isPlaying()) {
                        player.pause();
                        ibPlay.setImageResource(android.R.drawable.ic_media_play);
                        startThread();
                    } else {
                        player.play();
                        ibPlay.setImageResource(android.R.drawable.ic_media_pause);
                        startThread();
                    }
                    break;
                case R.id.ib_previous:
                    player.previous();
                    ibPlay.setImageResource(android.R.drawable.ic_media_pause);
                    startThread();
                    break;
                case R.id.ib_next:
                    player.next();
                    ibPlay.setImageResource(android.R.drawable.ic_media_pause);
                    startThread();
                    break;
            }
        }
    }

    //ListView点击事件
    private class OnItemClickListener implements AdapterView.OnItemClickListener {
        @Override
        public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
            player.play(i);
            ibPlay.setImageResource(android.R.drawable.ic_media_pause);
            startThread();
        }
    }

    //seekbar监听
    private class OnSeekBarChangeListener implements SeekBar.OnSeekBarChangeListener {

        @Override
        public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
            //当进度发生改变时【不适用】
            // 第三个参数boolean fromUser,区分是程序代码改变了进度,还是人为改变了进度
            //在人为拖拽时,进度在不停的改变,但是不需要持续相应
        }

        @Override
        public void onStartTrackingTouch(SeekBar seekBar) {
            //当开始拖拽进度条时
            isTrackingTouch = true;
        }

        @Override
        public void onStopTrackingTouch(SeekBar seekBar) {
            if (player.isStartWorking()) {
                //当结束拖拽进度条时
                player.seekTo(seekBar.getProgress());
                //标记为停止拖拽
                isTrackingTouch = false;
                ibPlay.setImageResource(android.R.drawable.ic_media_pause);
                startThread();
            }else{
                seekBar.setProgress(0);
            }
        }
    }

    @Override
    protected void onDestroy() {
        //解除与Service的绑定
        unbindService(conn);
        //停止更新进度的线程
        stopThread();
        super.onDestroy();
    }

    @Override
    protected void onStop() {
        //停止语句放在super之前
        startThread();
        //super语句不要删掉
        super.onStop();
    }

    @Override
    protected void onRestart() {
        //super语句不要删掉,启动工作放在super之后
        //启动的时候,让系统把自己的任务做完,然后再执行自己的代码
        super.onRestart();
        startThread();
    }

    /**
     * 与Service实现绑定时建立的连接对象
     * 声明成内部类或接口类型都可以
     */
    private ServiceConnection conn;
    /**
     * 调用Service中实现的功能的对象
     */
    private IMusicPlayer player;

    private class InnerServiceConnection implements ServiceConnection {

        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            player = (IMusicPlayer) iBinder;
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    }

    //更新进度的线程
    //在Activity中开启线程
    //【线程任务】
    //1、长期的循环
    //2、每一次循环休眠1s
    //3、每次循环时,调用service的功能,获取相关数据
    //--当前播放歌曲的索引
    //--当前播放歌曲的时长
    //--当前播放歌曲的时间
    //4、每次循环时,在主线程中更新UI
    private class UpdateProgressThread extends Thread {
        private boolean isRunning;

        //设置线程是否循环执行
        public void setRunning(boolean isRunning) {
            this.isRunning = isRunning;
        }

        int currentMusicIndex;
        int currentPosition;
        int duration;

        public void run() {
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    //更新控件
                    //更新歌曲标题
                    String musicTitle = musics.get(currentMusicIndex).getTitle();
                    tvCurrentMusicTitle.setText("正在播放:" + musicTitle);
                    //更新歌曲播放到的时间
                    tvCurrentTime.setText(CommonUtils.getFormattedTime(currentPosition));
                    //判断是否正在拖拽.当没有拖拽进度条时才更新
                    if (!isTrackingTouch) {
                        //更新进度条
                        int progress = currentPosition * 100 / duration;
                        seekBar.setProgress(progress);
                    }
                    //更新总时长
                    tvDuration.setText(CommonUtils.getFormattedTime(duration));
                }
            };

            while (isRunning) {
                //判断是否播放,解决暂停后再播放播放时间可能一瞬间变成0的bug
                if (player.isPlaying()) {
                    //获取数据
                    currentMusicIndex = player.getCurrentMusicPosition();
                    currentPosition = player.getCurrentPosition();
                    duration = player.getDuration();

                    Log.d("MUSIC", "CurrentPosition " + currentPosition + ",Duration " + duration);
                }
                //更新UI
                runOnUiThread(runnable);

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    //更新进度的线程
    private UpdateProgressThread updateProgressThread;

    //开启线程
    private void startThread() {
        if (updateProgressThread == null) {
            updateProgressThread = new UpdateProgressThread();
            updateProgressThread.setRunning(true);
            updateProgressThread.start();
        }
    }

    //停止更新线程
    private void stopThread() {
        if (updateProgressThread != null) {
            updateProgressThread.setRunning(false);
            //设置null,线程并不会停
            updateProgressThread = null;
        }
    }
}

PlayMusicService

public class PlayMusicService extends Service {
    private List<Music> musics;//音乐列表
    private int currentMusicIndex;//当前播放歌曲索引
    private int pausePosition;//歌曲暂停时间
    private boolean isPlayerWorking;//播放器是否在工作
    private MediaPlayer mediaPlayer;
    //播放工具是否已经开始工作,用于解决没有播放任何歌曲前就拖拽进度条的bug
    private boolean isStartWork;

    public PlayMusicService() {
        //初始化播放工具
        mediaPlayer = new MediaPlayer();
        //获取歌曲列表
        IDao<Music> dao = MusicDaoFactory.newInstance();
        musics = dao.getData();
    }

    @Override
    public IBinder onBind(Intent intent) {
        InnerBinder binder = new InnerBinder();
        return binder;
    }

    private class InnerBinder extends Binder implements IMusicPlayer {

        @Override
        public void play() {
            PlayMusicService.this.play();
        }

        @Override
        public void play(int position) {
            PlayMusicService.this.play(position);
        }

        @Override
        public void pause() {
            PlayMusicService.this.pause();
        }

        @Override
        public void previous() {
            PlayMusicService.this.previous();
        }

        @Override
        public void next() {
            PlayMusicService.this.next();
        }

        @Override
        public int getCurrentMusicPosition() {
            return currentMusicIndex;
        }

        @Override
        public int getCurrentPosition() {
            return mediaPlayer.getCurrentPosition();
        }

        @Override
        public int getDuration() {
            return mediaPlayer.getDuration();
        }

        @Override
        public boolean isPlaying() {
            return isPlayerWorking;
        }

        @Override
        public void seekTo(int percent) {
            PlayMusicService.this.seekTo(percent);
        }

        @Override
        public boolean isStartWorking() {
            return isStartWork;
        }
    }

    public void play() {
        try {
            mediaPlayer.reset();
            mediaPlayer.setDataSource(musics.get(currentMusicIndex).getPath());
            mediaPlayer.prepare();
            mediaPlayer.seekTo(pausePosition);
            mediaPlayer.start();
            //标记正在播放
            isPlayerWorking = true;
            //标记为已经开始工作,可以对进度条进行拖拽
            isStartWork = true;
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void play(int position) {
        currentMusicIndex = position;
        pausePosition = 0;
        play();
    }

    public void pause() {
        pausePosition = mediaPlayer.getCurrentPosition();
        mediaPlayer.pause();
        isPlayerWorking = false;
    }

    public void previous() {
        currentMusicIndex--;
        if (currentMusicIndex < 0) {
            currentMusicIndex = musics.size() - 1;
        }
        //清空暂停时间
        pausePosition = 0;
        play();
    }

    public void next() {
        currentMusicIndex++;
        if (currentMusicIndex > musics.size() - 1) {
            currentMusicIndex = 0;
        }
        //清空暂停时间
        pausePosition = 0;
        play();
    }

    private void seekTo(int percent) {
        if (isStartWork) {
            pausePosition = mediaPlayer.getDuration() * percent / 100;
            play();
        }
    }

    @Override
    public void onDestroy() {
        //释放资源
        mediaPlayer.release();
        mediaPlayer = null;
        super.onDestroy();
    }
}

源码下载

源码地址

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值