本博客是拆开代码讲解,大家可点击下载源码:源码下载链接
在我们的手机里最常见的莫过于休闲软件了,最常见的休闲方式也就是听音乐,看电影……今天我们来看看如何使用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;
这样功能就都完成了……