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

优化

优化用到的字符串
发送广播时,设置的 Action 的字符串至少用到了 3 次,所以建一个存放常量的类
在这里插入图片描述

public class Consts {
    public static final String SET_PLAY_STATE = "set play state";
}

在用到的时候可以这样写

filter.addAction(Consts.SET_PLAY_STATE);

类似的,把其他字符串也放进 Consts

显示歌曲信息的代码

【4】实现播放信息的显示
  1)显示当前正在播放的歌曲的标题和时长
    a)在 Service 的 play() 方法中发出广播,并在 Intent 对象中封装正在播放的歌曲的索引和总时长
    b)在 Activity 中接收对应的广播,并根据 Intent 对象中封装的数据进行显示

PlayMusicService

//播放
    private void play() {
        try {
            ......
            //发送广播要求界面显示为播放状态
            Intent intent = new Intent(Consts.SET_PLAY_STATE);
            intent.putExtra(Consts.EXTRA_CURRENT_MUSIC_INDEX, currentMusicIndex);
            intent.putExtra(Consts.EXTRA_DURATION, player.getDuration());
            sendBroadcast(intent);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

MainActivity

布局中增加显示时间的 TextView

private class InnerReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (Consts.SET_PLAY_STATE.equals(action)) {
                ibPlay.setImageResource(android.R.drawable.ic_media_pause);
                //获取正在播放的歌曲的索引
                int currentMusicIndex = intent.getIntExtra(Consts.EXTRA_CURRENT_MUSIC_INDEX,0);
                //设置显示当前正在播放的歌曲的标题和时长
                tvMusicInfo.setText(musics.get(currentMusicIndex).getTitle());
                tvMusicDuration.setText(CommonUtils.getFormattedTime(intent.getIntExtra(Consts.EXTRA_DURATION,0)));
            } else if (Consts.SET_PAUSE_STATE.equals(action)) {
                ibPlay.setImageResource(android.R.drawable.ic_media_play);
            }
        }
    }

其中 CommonUtils 是工具类,里边的 getFormattedTime是格式化时间显示的方法,我们在音乐播放器 2.0 中写过。

Consts 中还增加了其他一些常量。

到这里播放的歌曲信息就展示出来了
在这里插入图片描述

实现进度条的相关功能

  2)显示当前播放到的进度,即更新进度条与显示播放时间的 TextView
    a)声明并初始化 Seekbar 和显示播放时间的 TextView
    b)在 Service 中,使用内部类,声明更新进度的线程
        ------长时间的循环
        ------循环过程中,每隔 1s 休眠 1 次
        ------循环过程中,实时获取当前播放到的时间,及进度的百分比,然后使用广播进行发送
    c)在 Service 中,定义 startUpdateProgressThread() 方法和stopUpdateProgressThread(),并在 play() 方法的最后开启线程,在 pause() 方法的最后停止线程
    d)在 Activity 中,接收更新进度的线程,并根据封装的数据更新 SeekBar 和显示播放时间的 TextView

PlayMusicService

/**
     * 更新进度条的线程
     */
    private class InnerThread extends Thread {
        //线程的循环控制变量
        private boolean isRunning;

        public void setRunning(boolean isRunning) {
            this.isRunning = isRunning;
        }

        //不要在循环中new对象
        Intent intent = new Intent(Consts.UPDATE_PROGRESS);

        @Override
        public void run() {
            while (isRunning) {
                //计算相关数据
                //子线程做的事越多越好,主线程做的事越少越好
                int currentPosition = player.getCurrentPosition();
                int percent = currentPosition * 100 / player.getDuration();

                intent.putExtra(Consts.EXTRA_CURRENT_POSITION, currentPosition);
                intent.putExtra(Consts.EXTRA_PERCENT, percent);
                sendBroadcast(intent);
                //休眠
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    //更新进度的线程
    private InnerThread innerThread;

    /**
     * 开启更新进度的线程
     */
    private void startUpdateProgressThread() {
        if (innerThread == null) {
            innerThread = new InnerThread();
            innerThread.setRunning(true);
            innerThread.start();
        }
    }

    /**
     * 关闭更新进度的线程
     */
    private void stopUpdateProgressThread() {
        if (innerThread != null) {
            innerThread.setRunning(false);
            innerThread = null;
        }
    }

//播放
    private void play() {
        try {
            ......
            //开启更新进度的线程
            startUpdateProgressThread();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //暂停
    private void pause() {
        ......
        //停止更新进度的线程
        stopUpdateProgressThread();
    }

MainActivity

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ......
        //注册广播接收者
        receiver = new InnerReceiver();
        IntentFilter filter = new IntentFilter();
        filter.addAction(Consts.SET_PLAY_STATE);
        filter.addAction(Consts.SET_PAUSE_STATE);
        filter.addAction(Consts.UPDATE_PROGRESS);
        registerReceiver(receiver, filter);
    }

    private class InnerReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (Consts.SET_PLAY_STATE.equals(action)) {
                ......
            } else if (Consts.SET_PAUSE_STATE.equals(action)) {
                ......
            } else if (Consts.UPDATE_PROGRESS.equals(action)) {
                int currentPosition = intent.getIntExtra(Consts.EXTRA_CURRENT_POSITION, 0);
                int percent = intent.getIntExtra(Consts.EXTRA_PERCENT, 0);
                //更新控件
                tvMusicCurrentPosition.setText(CommonUtils.getFormattedTime(currentPosition));
                skMusicProgress.setProgress(percent);
            }
        }
    }

实现拖拽进度条

  3)实现拖拽进度条
    a)在 Activity 中,为 SeekBar 配置监听器
    b)在 Activity 中,在开始拖拽时,标记为正在拖拽
    c)在 Activity 中,在结束拖拽时,标记为停止拖拽,发出广播,使 Service 实现快进功能
    d)在 Activity 中,在更新进度时,判断是否正在拖拽,如果是,则不要更新进度条
    e)在 Service 中,声明 seekTo(int) 方法,实现快进功能
    f)在 Service 中,接收“快进”的广播,并在接收到时,调用 seekTo(int) 方法

MainActivity实现OnSeekBarChangeListener接口

public class MainActivity extends AppCompatActivity implements View.OnClickListener, AdapterView.OnItemClickListener, SeekBar.OnSeekBarChangeListener {
	......
    private void setListeners() {
        ......
        skMusicProgress.setOnSeekBarChangeListener(this);
    }
	......
    //进度条拖拽
    //标记是否正在标记进度条
    private boolean isTrackingTouch;

    @Override
    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
        
    }

    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {
        isTrackingTouch = true;
    }

    @Override
    public void onStopTrackingTouch(SeekBar seekBar) {
        isTrackingTouch = false;
        //当停止拖拽时发出广播,使service实现快进
        Intent intent = new Intent(Consts.SEEK_T0);
        intent.putExtra(Consts.EXTRA_SEEK_TO_PERCENT, skMusicProgress.getProgress());
        sendBroadcast(intent);
    }
	......
}

PlayMusicService

public class PlayMusicService extends Service {
	......
    @Override
    public void onCreate() {
        ......
        //注册广播接收者
        receiver = new InnerReceiver();
        IntentFilter filter = new IntentFilter();
        filter.addAction(Consts.PLAY_OR_PAUSE);
        filter.addAction(Consts.PLAY_APPOINT);
        filter.addAction(Consts.SEEK_T0);
        registerReceiver(receiver, filter);
    }
  	......
    /**
     * 快进到指定位置再开始播放
     *
     * @param percent 要播放到的百分比,取值例如50
     */
    private void seekTo(int percent) {
        //停止更新进度的线程
        stopUpdateProgressThread();
        //计算要快进到的位置,并作为暂停位置
        pausePosition = percent * player.getDuration() / 100;
        play();
    }

    /**
     * 内部广播接收者
     */
    private class InnerReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            //获取广播中的Action
            String action = intent.getAction();
            //判断Action
            if (Consts.PLAY_OR_PAUSE.equals(action)) {
            ......
            } else if (Consts.SEEK_T0.equals(action)) {
                //获取快进到的位置
                int percent = intent.getIntExtra(Consts.EXTRA_PERCENT, 0);
                seekTo(percent);
            }
        }
    }
	......
}

处理细节,修复bug

【5】处理细节,修复 Bug
  1)在没有开始播放任何一首歌之前,不应该拖拽进度条
  2)当界面被置于后台,则不应该再更新播放进度
  3)当界面被置于后台,如果自动切换到下一首歌曲,也不应该再重新开启更新进度的线程
  4)拖拽进度条时,如果没有获取到当前歌曲的总时长,或者有效的当前播放的位置,则不应该更新进度,表现为在播放过程中拖拽进度条可能会出现闪退
  5)退出应用程序时,应该释放资源

剩余的这些bug都是 2.0 处理过的,大家可以自行练习

源码下载

源码下载

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值