一、音乐播放器界面截图,录屏

模拟器

手机(测试中发现部分机型有闪退情况,截图展示的手机型号为荣耀9)

手机录屏

https://www.bilibili.com/video/av39894045/

二、功能代码

该音乐播放器实现的功能有,播放,暂停,停止,切换上一首,下一首,扫描手机内存所有目录下的所有音乐文件,时间进度条显示,拖动进度条快进快退,下面依次展示这几个部分的代码:

(1)播放暂停功能(使用同一个按钮,按下暂停改变为播放按钮,按下播放改变为暂停按钮)

        //播放暂停
        final ImageButton pause = (ImageButton) findViewById(R.id.pause);
        pause.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (!music_path.isEmpty()) {  // 如果已经点击了列表中的某个音乐,音乐路径是指定的
                    mediaPlayer.start();   //播放
                    isStop = false;
                    pause.setImageResource(android.R.drawable.ic_media_pause);  // 播放按钮和暂停按钮变换
                }
                if (mediaPlayer.isPlaying()) {
                    mediaPlayer.pause();  //暂停
                    isStop = true;
                    pause.setImageResource(android.R.drawable.ic_media_play);
                }
                if (music_path.isEmpty())  // 如果一开始没有点击列表选择音乐,音乐路径是空的,这时候需要给一个提示
                    Toast.makeText(getApplicationContext(), "点击音乐即可播放", Toast.LENGTH_SHORT).show();
            }
        });

(2)停止播放功能

        // 停止播放
        final ImageButton stop = (ImageButton)findViewById(R.id.stop);
        stop.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mediaPlayer.reset();  // 停止播放
                isStop = true;
            }
        });

(3)切换上一首和下一首功能

        //切换上一曲和下一曲
        final ImageButton previous = (ImageButton) findViewById(R.id.previous);
        previous.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                music_index--;
                changeMusic(music_index); // 调用changeMusic方法
                pause.setImageResource(android.R.drawable.ic_media_pause);// 因为切歌后就开始播放了,所以播放暂停按钮要置为暂停的图标
            }
        });
        final ImageButton next = (ImageButton) findViewById(R.id.next);
        next.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                music_index++;
                changeMusic(music_index);
                pause.setImageResource(android.R.drawable.ic_media_pause);
            }
        });
    }
    
    // changeMusic方法用于切换音乐
    private void changeMusic(int index) {
        if (index < 0) {  // 处理边界问题
            music_index = index = list.size() - 1;
        } else if (index > list.size() - 1) {
            music_index = index = 0;
        }
        music_path = MusicPath[index];  // 获取音乐文件的路经
        try {
            mediaPlayer.reset(); // 切歌之前先重置,释放掉之前的资源
            mediaPlayer.setDataSource(music_path); // 指定音频文件路径
            mediaPlayer.prepare(); // 让mediaPlayer进入准备状态
            mediaPlayer.start();  // 开始播放
        } catch (IOException e) {
            e.printStackTrace();
        }
        seekBar.setProgress(0); //将进度条初始化
        seekBar.setMax(mediaPlayer.getDuration());//设置进度条最大值为音乐总时间
        totalTime.setText(formatTime(mediaPlayer.getDuration())); //显示音乐总时长
        updateProgress();//更新进度条
    }

(4)扫描所有音乐文件,生成音乐列表并显示

        private String []MusicName = new String[100]; // 音乐文件名数组
        private String []MusicPath = new String[100]; // 音乐文件路径数组

        // 生成音乐列表
        list = new ArrayList<String>();
        try {
            Context context = this;
            Cursor cursor = context.getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,  null ,
                    null ,  null , MediaStore.Audio.Media.DEFAULT_SORT_ORDER);   // 使用系统提供的数据库查询接口,使用query方法查询所有音频文件
            int i = 0;
            if(cursor != null){
                while (cursor.moveToNext()){
                    MusicName[i] = new String(cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DISPLAY_NAME)));  // 获取音乐文件名
                    MusicPath[i] = new String(cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA)));   // 获取音乐文件路径
                    i = i + 1;
                }
            }
        }catch (Exception e){
            Toast.makeText(getApplicationContext(), "获取音乐失败", Toast.LENGTH_SHORT).show();;
        }
        int i = 0;
        while(MusicName[i] != null){
            list.add(MusicName[i]);  // 将文件名写入列表
            i++;
        }
        // 设置ListView样式
        ArrayAdapter<String> adapter = new ArrayAdapter<String>(MainActivity.this,
                android.R.layout.simple_list_item_single_choice,
                list);
        ListView li = (ListView) findViewById(R.id.listView1);
        li.setAdapter(adapter);
        li.setChoiceMode(ListView.CHOICE_MODE_SINGLE);

(5)时间进度条显示功能

    private SeekBar seekBar;       // 时间进度条组件
    private TextView currentTime; // 播放当前时间
    private TextView totalTime;  // 音乐的总时间
    private Timer timer;   // 定时器

    // 用于实现进度条更新的Handler
    private Handler mHandler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message message) {
            // 展示进度条和当前时间
            int progress = mediaPlayer.getCurrentPosition();
            seekBar.setProgress(progress);
            currentTime.setText(formatTime(progress));
            // 定时发送数据
            updateProgress();
            return true;
        }
    });
    private void updateProgress() {
        // 使用Handler每间隔1s发送一次消息,通知进度条更新
        Message msg = Message.obtain();
        // 用当前播放时间除以总时间获取播放进度
        int progress = mediaPlayer.getCurrentPosition();
        msg.arg1 = progress;
        mHandler.sendMessageDelayed(msg, 1000);
    }

    //formatTime方法用于对时间进行格式化,形式是mm:ss
    private String formatTime(int length) {
        Date date = new Date(length);
        SimpleDateFormat sdf = new SimpleDateFormat("mm:ss");
        String totalTime = sdf.format(date);
        return totalTime;
    }

(6)拖动进度条快进快退功能

    seekBar.setOnSeekBarChangeListener(new MoveSeekBar());
    private boolean isSeekBarChanging;  //判断是否在滑动进度条,这个时候定时器是停止的

    // 滑动进度条
    public class MoveSeekBar implements SeekBar.OnSeekBarChangeListener {
        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
        }
        //滑动时,应当暂停后台定时器
        public void onStartTrackingTouch(SeekBar seekBar) {
            isSeekBarChanging = true;
        }
        //滑动结束后,重新设置值
        public void onStopTrackingTouch(SeekBar seekBar) {
            isSeekBarChanging = false;
            mediaPlayer.seekTo(seekBar.getProgress());
        }
    }

三、coding链接

https://git.dev.tencent.com/ysh1998/MusicPlayer.git

四、apk链接

https://dev.tencent.com/u/ysh1998/p/MusicPlayer/git/blob/master/app/app-release.apk