Android中的MediaPlayer——音乐播放器实践

  本博客是拆开代码讲解,大家可点击下载源码:源码下载链接
  在我们的手机里最常见的莫过于休闲软件了,最常见的休闲方式也就是听音乐,看电影……今天我们来看看如何使用MediaPlayer制作一个简单的音乐播放器。
  博主是大菜鸟一只,不要嫌弃我做的差啊……大牛们多多指教ing^
  首先我们来认识一下MediaPlayer。

MediaPlayer

  学习MediaPlayer的基本使用,我们只需要看懂下面这张图就OK。
  
这里写图片描述

  使用MediaPlayer,我们首先要创建它的对象,然后从图中我们可以看到,首先调用reset()方法,然后使用setDataSource()方法加载要播放的媒体文件。调用prepare()方法进入准备状态,然后调用start()方法就可以开始播放媒体文件。其实就是这么简单了。如果我们想要播放暂停,我们可以调用pause()方法暂停,如果想停止就调用stop()方法。暂停和停止的区别就不用说了吧。其实看到这,我们就已经掌握了MediaPlayer的基本使用了。接下来,我们来看我们的小型音乐播放器,MediaPlayer的进一步使用我们在实例中学习。

简易音乐播放器

功能描述:

1. 布局说明,有音乐显示列表,音乐进度显示,以及控制按钮。
2. 音乐列表中显示音乐图片,有则显示,无则默认显示;显示歌名;显示歌手名称。
3. 有进度显示条,以及音乐的总时间和当前时间。
4. 有播放(暂停),上一首,下一首按钮:

  • 点击音乐列表中的歌曲开始播放,点击暂停按钮暂停,在此点击继续播放。
  • 点击上一首歌曲播放上一首,如果当前歌曲为第一首,则播放第一首歌曲。
  • 点击下一首歌曲播放下一首。
  • -

功能演示:

  我们首先来看一下功能演示…没有发视频,只能看不能听哦……
ListView列表显示:

这里写图片描述

点击ListView列列表歌曲开始播放:

这里写图片描述

点击播放(暂停)按钮,暂停或播放:

这里写图片描述

点击下一首播放下一首歌曲:

这里写图片描述

点击上一首播放上一首歌曲:

这里写图片描述

  功能演示完毕,进入正题,功能实现……

功能实现:

  本博客是拆开代码讲解,大家可点击下载源码:源码下载链接
  我们来讲解一下实现的思路。首先定义总体布局,然后将显示的歌曲列表显示在ListView中。这时,我们就是实现歌曲的基本播放了,首先我们尝试播放歌曲,不添加什么按钮功能。然后我们将定义SeekBar显示歌曲的进度,实现这一功能,并将播放时间和总时长显示出来。最后我们再来添加播放(暂停),上一首,下一首按钮功能。我们来看代码:
  
1. 定义总体布局:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/main_background"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <ListView
        android:id="@+id/music_list"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1">

    </ListView>


    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="right">

        <TextView
            android:id="@+id/textview_duration"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="00:00"
            android:textColor="@color/white" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="/"
            android:textColor="@color/white" />

        <TextView
            android:id="@+id/textview_current_time"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text=" 00:00"
            android:textColor="@color/white" />
    </LinearLayout>

    <SeekBar
        android:id="@+id/seekbar"
        android:layout_width="300dp"
        android:layout_height="wrap_content"
        android:layout_gravity="center" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@null"
        android:gravity="center"
        android:orientation="horizontal">

        <ImageButton
            android:id="@+id/button_previous_music"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:background="@drawable/imagebutton_previous_music" />

        <ImageButton
            android:id="@+id/button_play"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:layout_marginLeft="100dp"
            android:layout_marginRight="100dp"
            android:background="@drawable/imagebutton_music_play" />

        <ImageButton
            android:id="@+id/button_next_music"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            android:background="@drawable/imagebutton_next_music" />
    </LinearLayout>

</LinearLayout>

布局结果如下:
这里写图片描述

2. 我们将获取的歌曲列表添加到ListView中。

  首先定义ListView中每条item的布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="horizontal">

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="5dp"
        android:src="@mipmap/img_default_singer" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <TextView
            android:id="@+id/textview_music_name"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:padding="5dp"
            android:text="音乐名称"
            android:textColor="@color/white"
            android:textSize="15sp" />

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@mipmap/img_favourite_normal" />

            <TextView
                android:id="@+id/textview_author"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="10dp"
                android:gravity="center"
                android:padding="5dp"
                android:text="作家"
                android:textColor="@color/white"
                android:textSize="15sp" />
        </LinearLayout>

    </LinearLayout>
</LinearLayout>

  定义一个Adapter,将数据与布局适配,这里跟我们使用方法相同,唯一不同点就是使用了一个MediaMetadataRetriever 类,来获取媒体文件包含的信息。具体用法可以看代码当注释。

public class MusicListAdapter extends BaseAdapter {

    private LayoutInflater mInflater;
    protected List<File> mFiles;

    public MusicListAdapter(LayoutInflater mInflater, List<File> files) {
        this.mInflater = mInflater;
        mFiles = files;
    }

    @Override
    public int getCount() {
        return mFiles.size();
    }

    @Override
    public Object getItem(int position) {
        return position;
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup viewGroup) {
        ViewHolder viewHolder = null;
        if (convertView == null) {
            viewHolder = new ViewHolder();
            convertView = mInflater.inflate(R.layout.item_music, null);
            viewHolder.textviewMusicName = (TextView) convertView.findViewById(R.id.textview_music_name);
            viewHolder.textviewAuthor = (TextView) convertView.findViewById(R.id.textview_author);
            convertView.setTag(viewHolder);
        }
        viewHolder = (ViewHolder) convertView.getTag();
        viewHolder.textviewMusicName.setText(mFiles.get(position).getName());

        //通过这个类来获取Mmp3文件的内容,包括作者,图片等…我们这里只获取作者,没有作者的我们定义为“<未知>”
        MediaMetadataRetriever mmr = new MediaMetadataRetriever();
        //获得文件的路径
        mmr.setDataSource(mFiles.get(position).getAbsolutePath());
        // 调用extractMetadata()方法,通过参数来货去不同的信息,具体参数可以看API
        String author = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ARTIST);
        if (author != null) {
            viewHolder.textviewAuthor.setText(author);
        } else {
            viewHolder.textviewAuthor.setText("<未知>");
        }
        return convertView;
    }

    class ViewHolder {
        TextView textviewMusicName;
        TextView textviewAuthor;
    }
}

  在Activity中我们要向适配器传入文件集合。我们看Activity中获取的文件集合,获得集合后我们要将mp3文件筛选出来,虽然我们访问的是标准SD卡的音乐文件夹,但是,其中可能会有隐藏文件,所以我们通过一个方法筛选下:

    /*
    筛选MP3文件的类
     */
    class MusicFilter implements FilenameFilter {
        public boolean accept(File dir, String filename) {
            return (filename.endsWith(".mp3"));
        }
    }
//MAinActivity的部分代码
        //获得音乐的文件列表
        File musicDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC);
        Log.d("data", "音乐的路径是:"+musicDir.getAbsolutePath());
        //将MP3文件筛选出来
        musicFilter = new MusicFilter();
        mFiles = new ArrayList<File>();
        mPreFiles = musicDir.listFiles();
        for(File file:mPreFiles){
            if(musicFilter.accept(musicDir, file.getName())){
                mFiles.add(file);
            }
        }

        //将mp3文件添加到ListView中
        mAdapter = new MusicListAdapter(getLayoutInflater(),mFiles );
        mListViewMusic.setAdapter(mAdapter);

  这样我们就完成将歌曲添加列表的功能了。
3. 实现播放的功能。
  播放功能的实现我们不能再Avtivity中,如果添加在Activity中,界面退出,音乐就停止了。为了实现音乐的后台运行,我们通过使用Service。在ListView的点击事件中添加开启服务,来实现服务的播放。

public class MusicService extends Service {
    private MediaPlayer player;
     public int onStartCommand(Intent intent, int flags, int startId) {
         startNewMusic(intent);
         return super.onStartCommand(intent, flags, startId);
     }
     private void startNewMusic(Intent intent) {
        String musicPath = intent.getStringExtra("MusicName");
        if (player == null) {
            player = new MediaPlayer();
        }
        player.reset();//复位播放器
        try {
            player.setDataSource(musicPath);//设置播放mp3文件的路径
            player.prepare();//播放准备
            player.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
                @Override
                public void onPrepared(MediaPlayer mediaPlayer) {
                    player.start();//播放开始         
                }
            });
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}   

4. 定义SeekBar显示歌曲的进度,实现这一功能,并将播放时间和总时长显示。
  播放的进度和时长我们可以通过MediaPlayer类的getDuration()方法获得歌曲的总时间和getCurrentPosition()方法获得当前播放的时间。因为我们的音乐是在Service中播放的,播放的进度时长只能在Service中知道,Activity中并不知道,为了实现Service联系Activity,我们通过广播完成。
在Activity中当定义一个广播:

    /*
    定义广播,并在广播中解析收到的消息。
     */
    public class MusicBroadcast extends BroadcastReceiver {
        public static final String MUSIC_TIME_ACTION = "com.lishuang.musictime";
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.d("data", "接收到一个广播 ");
            int  type= intent.getIntExtra("type", 0);
            switch (type){
                case MusicService.DURATION_TYPE:
                    int time = intent.getIntExtra("time", 0);
                    Log.d("data", "这是总时间"+time);
                    //将时间转化为分钟秒
//                    int durationMinute = time/1000/60;
//                    int durationSecond  = time/1000%60;
                    Date dateDuration = new Date(time);
                    SimpleDateFormat formatDuration = new SimpleDateFormat("mm:ss");
                    mSeekBar.setMax(time);
//                    mTextViewDuration.setText(durationMinute+":"+durationSecond);
                    mTextViewDuration.setText(formatDuration.format(dateDuration));
                    break;
                case MusicService.CURRENT_TIME_TYPE:
                    int currenttime=intent.getIntExtra("time", 0);
                    //将时间转化为分钟秒
//                    int dcurrenttimeMinute = currenttime/1000/60;
//                    int currenttimeSecond  = currenttime/1000%60;
                    Date dateCurrentTime = new Date(currenttime);
                    SimpleDateFormat formatCurrent = new SimpleDateFormat("mm:ss");
                    Log.d("data", "这是当前时间"+currenttime);
                    mSeekBar.setProgress(currenttime);
//                    mTextViewCurrentTime.setText(dcurrenttimeMinute+":"+currenttimeSecond);
                    mTextViewCurrentTime.setText(formatCurrent.format(dateCurrentTime));
                    break;
                default:
                    break;
            }
        }
    }

  注册广播,将广播与Activity绑定:

    //注册一个广播,用于服务练习Activity,设置SeekBar的进度
        musicBroadcast = new MusicBroadcast();
        IntentFilter filter = new IntentFilter();
        filter.addAction("com.lishuang.musictime");
        registerReceiver(musicBroadcast,filter);

  取消注册广播:

        @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(musicBroadcast);//取消注册广播
    }

  在MusicService中发送广播,这里注意:发送广播使用Intent,但是,我们获得的时间有两种:当前时间和总时长。是以我们这里要定义一个“type”类型来区分两种时间。

    //当前时间是在MusicService中定义一个线程获得的。
  @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
    class MusicThread extends Thread {
        @Override
        public void run() {
            //获得当前的时长
            while (player.isPlaying()) {
                int time = player.getCurrentPosition();
                Intent intent = new Intent("com.lishuang.musictime");
                intent.putExtra("time", time);
                intent.putExtra("type", CURRENT_TIME_TYPE);
                sendBroadcast(intent);
                //每隔一秒发送一次
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
     /*
    开始播放一首新的音乐的方法,这里获得总时间,并开启获得当期那时间的线程。
     */
    private void startNewMusic(Intent intent) {
        String musicPath = intent.getStringExtra("MusicName");
        if (player == null) {
            player = new MediaPlayer();
        }
        player.reset();
        try {
            player.setDataSource(musicPath);
            player.prepare();
            player.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
                @Override
                public void onPrepared(MediaPlayer mediaPlayer) {
                    player.start();

                    int time = player.getDuration();//获得所有的时间
                    Intent intent = new Intent("com.lishuang.musictime");
                    intent.putExtra("time", time);
                    intent.putExtra("type", DURATION_TYPE);
                    sendBroadcast(intent);
                    MusicThread thread = new MusicThread();
                    thread.start();
                }
            });
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

5. 添加播放(暂停),上一首,下一首按钮功能。
  定义三个按钮,添加点击事件。在点击事件中启动服务,在服务中操作。
播放(暂停)按钮:

    @Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.button_play:
                mIsPause=!mIsPause;//定义全局变量,记录暂停状态。
                Log.d("pause", "点击按钮后的暂停状态"+mIsPause);
                Intent intent1 = new Intent(MainActivity.this,MusicService.class);
                intent1.putExtra("start_type", Config.START_TYPE_OPERATION);
                intent1.putExtra("MusicName", mFiles.get(currentPosition).getAbsolutePath());
                intent1.putExtra("operation",Config.OPEARTION_PLAY );
                intent1.putExtra("state", mIsPause);
                startService(intent1);
                // 切换按钮的图片
                setPlayOrPauseButtonImage();
                break;
            default:
                break;
        }
    }
    /*
    设置开始(暂停按钮的图片)
     */
    private void setPlayOrPauseButtonImage() {
        if(!mIsPause){
            mButtonPlay.setBackgroundResource(R.mipmap.img_lockscreen_pause_normal);
        }else  if(mIsPause){
            mButtonPlay.setBackgroundResource(R.mipmap.img_lockscreen_play_normal);
        }
    }

下一首按钮:

@Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.button_play:
            currentPosition = currentPosition+1;//全局变量,记录当前歌曲播放的位置
                Intent intent2 = new Intent(MainActivity.this,MusicService.class);
                intent2.putExtra("start_type", Config.START_TYPE_OPERATION);
                intent2.putExtra("operation",Config.OPEARTION_NEXT_MUSIC);
                intent2.putExtra("MusicName", mFiles.get(currentPosition).getAbsolutePath());
                startService(intent2);
                mIsPause=false; //设置暂停的状态
                setPlayOrPauseButtonImage();//设置播放(暂停)按钮的图片
                Log.d("currentPosition", "播放下一首时, 当前播放的文件的位置!"+currentPosition);
                break;
            default:
                break;
        }
    }

上一首按钮:

@Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.button_play:
            case  R.id.button_previous_music:
                Intent intent3 = new Intent(MainActivity.this,MusicService.class);
                intent3.putExtra("start_type", Config.START_TYPE_OPERATION);
                intent3.putExtra("operation",Config.OPEARTION_PREVIOUS_MUSIC);
                if(currentPosition>0){
                    currentPosition = currentPosition-1;
                }
                intent3.putExtra("MusicName", mFiles.get(currentPosition).getAbsolutePath());
                Log.d("currentPosition", "播放上一首时, 当前播放的文件的位置!"+currentPosition);
                startService(intent3);
                mIsPause=false; //设置暂停的状态
                setPlayOrPauseButtonImage();//设置播放(暂停)按钮的图片
                break;
            default:
                break;
        }
    }

  服务中接收消息,实现按钮功能:

     case Config.START_TYPE_OPERATION: {
                int operation = intent.getIntExtra("operation", Config.OPEARTION_PLAY);
                switch (operation) {
                    case Config.OPEARTION_PLAY:
                        playOrPauseMusic(intent);//开始和暂停播放音乐
                        break;
                    case Config.OPEARTION_NEXT_MUSIC:
                        if (player != null) {
                            player.stop();
                            player=null;
                            startNewMusic(intent);
                        }
                        break;
                    case Config.OPEARTION_PREVIOUS_MUSIC:
                        if (player != null) {
                            player.stop();
                            player=null;
                            startNewMusic(intent);
                        }
                        break;
                }
            }
            break;

   这样功能就都完成了……

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小_爽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值