在Android中播放音频文件一般使用MediaPlayer类来实现,它对多种格式的音频文件提供了非常全面的控制方法,从而使得播放音乐的工作变得简单
setDataSource():设置要播放的音频文件的位置
prepare():在开始播放之前调用这个方法完成准备工作
start():开始或继续播放
pause():暂停播放
reset():将MediaPlayer对象重置到刚刚创建的状态
seekTo():从指定位置开始播放
stop():停止播放音频,调用之后MediaPlayer对象无法再播放音频
release():释放掉与MediaPlayer对象相关的资源
isPlaying():判断当前MediaPlayer是否正在播放
getDuration():获取载入的音频文件的时长
播放
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
private Button mButton;
private MediaPlayer player=new MediaPlayer();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mButton= (Button) findViewById(R.id.button_musicplay);
mButton.setOnClickListener(this);
mButtonPause= (Button) findViewById(R.id.button_musicpause);
mButtonPause.setOnClickListener(this);
player.reset();
File path= Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC);
File[]files=path.listFiles();
try {
player.setDataSource(files[0].getAbsolutePath());
player.prepare();
player.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
mp.start();
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
}
但是要实现一个功能比较齐全的仅仅靠在主活动上加点击事件是不可能的,因为还要实现音乐可以后台播放,并可以将播放过程用进度条显示在主活动上,通过拖拉进度条可以是音乐从所拖动的位置继续播放。要实现这么些功能,就需要用到Android中的服务和广播接收器。
首先搭建一个音乐播放器的主界面
<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:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
android:orientation="vertical" tools:context=".MainActivity">
<ListView
android:id="@+id/listview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1">
</ListView>
<SeekBar
android:id="@+id/seekBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/textview_currenttime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="当前时间"/>
<TextView
android:id="@+id/textview_alltime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:text="总时间"/>
</RelativeLayout>
<Button
android:id="@+id/button_musicpause"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="暂停/播放"/>
</LinearLayout>
ListView中的布局为
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/imageview"
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@mipmap/ic_launcher"/>
<TextView
android:id="@+id/textview_artist"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="艺术家"/>
<TextView
android:id="@+id/textview_line"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="——"/>
<TextView
android:id="@+id/textview_musicname"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="歌曲名"/>
</LinearLayout>
在主活动中添加三个点击事件,分别是ListView、开始暂停按钮、SeekBar进度条,还有一个广播接收器,用来接收广播并对UI上的空间进行处理。
package com.example.administrator.mymediaplayer;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.MediaPlayer;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.ListView;
import android.widget.SeekBar;
import android.widget.TextView;
import com.example.administrator.mymediaplayer.adapter.MusicAdapter;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.logging.SimpleFormatter;
public class MainActivity extends AppCompatActivity{
private Button mButton;
private Button mButtonPause;
private ListView mListView;
private TextView mTextviewAll;
private TextView mTextviewCurrent;
private SeekBar mSeekBar;
private File[]musics;
private MediaPlayer player=new MediaPlayer();
private MusicReceiver musicReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextviewAll= (TextView) findViewById(R.id.textview_alltime);
mTextviewCurrent= (TextView) findViewById(R.id.textview_currenttime);
//开始暂停按钮的点击事件
mButtonPause= (Button) findViewById(R.id.button_musicpause);
mButtonPause.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent=new Intent(getApplicationContext(),MusicService.class);
intent.putExtra("type",Config.STOP_MUSIC);
startService(intent);
}
});
//SeekBar的点击事件,将进度信息传送给后台服务
mSeekBar= (SeekBar) findViewById(R.id.seekBar);
mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
Intent intent=new Intent(getApplicationContext(),MusicService.class);
intent.putExtra("type",Config.START_SEEK_TO);
intent.putExtra("progress",seekBar.getProgress());
startService(intent);
}
});
//找到sdCard
File sdCard=Environment.getExternalStorageDirectory();
//找到sdCard下的存放音乐的文件夹
File musicFiles = new File(sdCard,"Music");
//得到音乐文件夹下的所有歌曲
musics=musicFiles.listFiles();
//通过点击放置歌曲的ListView中的某一条信息使得音乐可以在后台播放
mListView= (ListView) findViewById(R.id.listview);
mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Intent intent=new Intent(getApplicationContext(),MusicService.class);
intent.putExtra("type",Config.START_MUSIC);
//把某一首歌的路径传送给服务类
intent.putExtra("musicname",musics[position].getAbsolutePath());
//启动服务
startService(intent);
}
});
//定义ListView的适配器
MusicAdapter adapter=new MusicAdapter(getLayoutInflater(),musics);
//将适配器加载到ListView中
mListView.setAdapter(adapter);
//广播接收器的注册
musicReceiver=new MusicReceiver();
IntentFilter filter=new IntentFilter("com.music");
registerReceiver(musicReceiver,filter);
}
//广播接收器的取消注册
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(musicReceiver);
}
//广播接收器类
class MusicReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
//接收到的广播的格式,通过不同的格式执行不同的操作
int type=intent.getIntExtra("type", Config.ALL_TIME);
//定义显示歌曲播放和总时间的格式
SimpleDateFormat format=new SimpleDateFormat("mm:ss");
switch (type){
//得到歌曲的总时间,并设置进度条总的长度,作为给播放时间进度条的位置的参考
case Config.ALL_TIME:
int alltime=intent.getIntExtra("alltime",Config.ALL_TIME);
mSeekBar.setMax(alltime);
String timeall=format.format(alltime);
mTextviewAll.setText(timeall);
break;
//得到当前播放时间,并将其设置在进度条上
case Config.CURRENT_TIME:
int time=intent.getIntExtra("time",Config.CURRENT_TIME);
mSeekBar.setProgress(time);
String timecurrent=format.format(time);
mTextviewCurrent.setText(timecurrent);
break;
default:
break;
}
}
}
}
然后既然有ListView,就需要一个Adapter类
package com.example.administrator.mymediaplayer.adapter;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.MediaMetadata;
import android.media.MediaMetadataRetriever;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import com.example.administrator.mymediaplayer.R;
import java.io.File;
/**
* Created by Administrator on 2015/9/10.
*/
public class MusicAdapter extends BaseAdapter {
private LayoutInflater mInflater;
private File[]musics;
public MusicAdapter(LayoutInflater mInflater, File[] musics) {
this.mInflater = mInflater;
this.musics = musics;
}
@Override
public int getCount() {
return musics.length;
}
@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 parent) {
ViewHolder viewHolder=null;
if (convertView==null){
convertView=mInflater.inflate(R.layout.activity_music,null);
viewHolder=new ViewHolder();
viewHolder.textView= (TextView) convertView.findViewById(R.id.textview_musicname);
viewHolder.textViewArtist= (TextView) convertView.findViewById(R.id.textview_artist);
viewHolder.textViewLine= (TextView) convertView.findViewById(R.id.textview_line);
viewHolder.imageView= (ImageView) convertView.findViewById(R.id.imageview);
convertView.setTag(viewHolder);
}else {
viewHolder= (ViewHolder) convertView.getTag();
}
MediaMetadataRetriever metadata=new MediaMetadataRetriever();
metadata.setDataSource(musics[position].getAbsolutePath());
String artist=metadata.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ARTIST);
if (artist==null){
viewHolder.textViewArtist.setText("UnKnow");
}else {
viewHolder.textViewArtist.setText(artist);
}
byte[]image=metadata.getEmbeddedPicture();
if (image==null){
viewHolder.imageView.setImageResource(R.mipmap.ic_launcher);
}else {
Bitmap bitmap= BitmapFactory.decodeByteArray(image,0,image.length);
viewHolder.imageView.setImageBitmap(bitmap);
}
viewHolder.textView.setText(musics[position].getName());
return convertView;
}
class ViewHolder{
TextView textView;
TextView textViewArtist;
TextView textViewLine;
ImageView imageView;
}
}
中间获取歌曲的作者和图片的代码其实放在这里不是很合适,但是比较简单,不需要来回传递信息。
然后就需要一个后台服务类来进行后台操作,在后台中对活动中传回来的信息做相应的后台服务
package com.example.administrator.mymediaplayer;
import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.util.Log;
import java.io.IOException;
/**
* Created by Administrator on 2015/9/10.
*/
public class MusicService extends Service {
private MediaPlayer player;
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
int type=intent.getIntExtra("type",Config.START_MUSIC);
switch (type){
case Config.START_MUSIC:
startMusic(intent);
break;
case Config.START_SEEK_TO:
int progress=intent.getIntExtra("progress",0);
player.seekTo(progress);
break;
case Config.STOP_MUSIC:
if (player.isPlaying()){
player.pause();
}else{
player.start();
}
new MusicPlayThread().start();
default:
break;
}
return super.onStartCommand(intent, flags, startId);
}
private void startMusic(Intent intent) {
String musicpath=intent.getStringExtra("musicname");
if (player==null){
player=new MediaPlayer();
}
player.reset();
try {
player.setDataSource(musicpath);
player.prepare();
player.start();
} catch (IOException e) {
e.printStackTrace();
}
int time=player.getDuration();
Intent intent2=new Intent("com.music");
intent2.putExtra("type",Config.ALL_TIME);
intent2.putExtra("alltime",time);
sendBroadcast(intent2);
new MusicPlayThread().start();
}
//用来发送当前广播信息的线程
class MusicPlayThread extends Thread{
@Override
public void run() {
while (player.isPlaying()){
int currenttime=player.getCurrentPosition();
Intent intent=new Intent("com.music");
intent.putExtra("type",Config.CURRENT_TIME);
intent.putExtra("time",currenttime);
sendBroadcast(intent);
//由于Android对时间是一毫秒计算,所以需要休眠一段时间,降低发送频率,不至于广播积压
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}