本人初学Android,最近做了一个实现安卓简单音乐播放功能的播放器,收获不少,于是便记录下来自己的思路与知识总结,重温自己的探索之路。
1 那么先上几张截图来看下程序实现效果吧


2 可以看出基本实现的功能有
暂停(播放),上(下)一首,停止播放
点击列表中歌曲进行播放,点击按钮以弹出歌曲列表
屏幕上显示歌曲名称,演唱者,专辑图片
显示播放进度条,拖拽进度条以控制播放
通知栏中显示相关信息,并可在通知栏上控制播放
3 总体开发思路

一共有两个Activity界面(分别是主界面和播放细节界面),一个用以在后台播放音乐的Service,一个可以和用户进行交互Notificatication。 为了能够使得活动与活动,活动与服务,活动与通知之间通信,采取发送广播的做法,由于我在MainActivity,PlayActivity,MusicService中都获取了歌曲信息列表,所以在他们之间直接传递歌曲下标location和状态isPlaying即可
核心是由MainActivity通过startService方法控制Service来进行播放
- MainActivity中点击事件发生后更新自己的UI,同时startService
- PlayActivity中点击事件发生后更新自己UI,发送广播给MainActivity,让MainActivity更新UI,startService,从而实现PlayActivity间接控制播放
- Notification点击事件发生后发生广播给MainActivity,让MainActivity更新UI,startService,从而实现Notification间接控制播放
4 重要功能实现
- 内容提供器用法一般有两种: 使用现有的内容提供器和创建自己的内容提供器。
- 在使用系统自带的内容提供器,注意要在AndroidManifest中添加权限声明,如果是危险权限,还要进行运行时权限声明,这里由于本人手机低于Android6.0系统,顾没有进行运行时申请。
//query方法第一个参数为 Uri:这个Uri代表要查询的数据库名称加上表的名称,这里我们获取所有的歌曲信息,后面四个参数为查询条件和数据排列条件,这里我们都使用null
Cursor
cursor=context.getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, null, null)
for(int i=0;i<cursor.getCount();i++){
cursor.moveToNext();
Mp3Info mp3Info=new Mp3Info();
mp3Info.setUrl(cursor.getString(cursor .getColumnIndex(MediaStore.Audio.Media.DATA)));
mp3InfoList.add(mp3Info);
- SeekBar的实现注意声明android:thumb="@drawable/media_player_progress_button"//滑动的光标
- 按钮ImageButton背景声明我采用如下:app:srcCompat="@drawable/play"/> ,这样在活动中便可通过button.setDateResource(R.drawable....)来实现背景的切换,如果界面编程中直接使用setBackground方法,到时候会造成按钮背景的重叠而不是切 换的效果(亲测)
- 使用ListIView的时候注意要在活动中setAdapter(),并设置响应子项点击事件(通过开启服务来播放点击的那首音乐)
Intent intent=new Intent(MainActivity.this,PlayActivity.class);
intent.putExtra("state",isPlaying);
intent.putExtra("location",location);
startActivity(intent);
//传递歌曲下标location并启动服务
Intent intent=new Intent(MainActivity.this,MusicService.class);
intent.putExtra("tag",0);
intent.putExtra("location",location);
startService(intent);
5 实现后台自动播放下一首音乐 :只需为MediaPlayer对象设置setOnCompletionListener()方法即可,在方法中使歌曲下标+1,播放即可,自动播放下一首后记得发送广播给活动和Notification以更新UI
//设置自动播放下一首
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mediaPlayer) {
if(mediaPlayer!=null){
try {
if (++location < mp3InfoList.size()) {
mediaPlayer.reset();
mediaPlayer.setDataSource(MusicService.this, Uri.parse(mp3InfoList.get(location).getUrl()));
mediaPlayer.prepare();
mediaPlayer.start();
}
}catch (Exception e){
e.printStackTrace();
}
}
6 实现显示播放进度功能:在Service中创建一个继承于AsyncTask的类,在该类子线程中线程每0.5秒发送一次带有当前播放进度的broadcast给活动界面以更新UI
- 为SeekBar设置点击拖动事件,通知seetOnSeekBarChangeListener(...)方法,其中OnSeekBarChangeListener类对象需重写三个方法,在 public void onStop TrackingTouch(SeekBar seekBar)方法中发送广播给Service(带有Progress信息)
- 在Service中接收progress信息调用MediaPlayer.seekTo(progress)方法来响应拖动事件
//为SeekBar设置点击拖动事件
seekBar.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) {
int progress=seekBar.getProgress();
Intent intent=new Intent(MainActivity.this, MusicService.class);
intent.putExtra("tag",2);
intent.putExtra("progress",progress);
startService(intent);
}
});
remoteViews = new RemoteViews(getPackageName(), R.layout.remoteviews_item_dl);//设置RemoteViews的布局
remoteViews.setTextViewText(R.id.tv_filename, getPackageName()); // 设置标题
remoteViews.setTextViewText(R.id.tv_content, "this is notification"); // 设置内容
remoteViews.setImageViewBitmap(R.id.notification_imageView,bitmap);//设置控件Bitmap
// 设置点击事件响应结果为发送广播
remoteViews.setOnClickPendingIntent(R.id.notification_next,PendingIntent.getBroadcast(this,0,
new Intent("com.example.musicplayer.notification.next").putExtra("tag2",2), PendingIntent.FLAG_UPDATE_CURRENT));
Notification notification=new NotificationCompat.builder(this).setContent(remotoViews).build();//构建出带有特定布局的通知
manager.notify(0,notification);//显示通知
- 建立一个数组存储listview上显示的数据
- 获取到集合数据
- 创建SimpleAdapter实例,参数解读(context,数据数组,listVIew item的布局,key数组,布局id数组),将key储存的数据赋予布局中相应id控件
- 为listView设置Adapter
- dialog.setView(dialog 框架布局),dialog.show()
- listView设置点击事件(发送带有点击item下标信息的广播给MainActivity)
5 开发思路的实现细节
1 首先新建保存mp3音乐相关属性信息的实体类Mp3Info(保存url,title,artist等等)
ublic class Mp3Info {
private String url;//路径
private String title;//歌曲名
private String artist;//艺术家
private long duration;//歌曲时长
private long id;
private long albumId;//以上两种Id用以获取专辑图片
public Mp3Info(){
}
public Mp3Info(String url,String title,String artist,long duration,long id,long albumId){
this.url=url;
this.title=title;
this.artist=artist;
this.duration=duration;
this.id=id;
this.albumId=albumId;
}
public void setUrl(String url){
this.url=url;
}
public void setTitle(String title){
this.title=title;
}
public void setArtist(String artist){
this.artist=artist;
}
public void setDuration(long duration){
this.duration=duration;
}
public void setId(long id){this.id=id;}
public void setAlbumId(long albumId){this.albumId=albumId;}
public String getUrl(){
return url;
}
public String getTitle(){
return title;
}
public String getArtist(){
return artist;
}
public long getDuration(){
return duration;}
public long getId() {
return id;
}
public long getAlbumId() {
return albumId;
}
}
2 新建MediaUtil类用于从安卓媒体库中获取歌曲信息并保存在程序的List中,提供静态方法给其他代码来直接复用
public class MediaUtil {
//获取专辑封面的Uri
private static final Uri albumArtUri = Uri.parse("content://media/external/audio/albumart");
//从安卓媒体库中获取歌曲信息并保存在程序的List中,并提供静态方法给其他类来直接获得生成的歌曲列表信息
public static List<Mp3Info>getMp3InfoList(Context context){
Cursor cursor=context.getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, null, null,
null);
List<Mp3Info>mp3InfoList=new ArrayList<>();
for(int i=0;i<cursor.getCount();i++){
cursor.moveToNext();
Mp3Info mp3Info=new Mp3Info();
mp3Info.setUrl(cursor.getString(cursor
.getColumnIndex(MediaStore.Audio.Media.DATA)));
mp3Info.setTitle(cursor.getString(cursor
.getColumnIndex(MediaStore.Audio.Media.TITLE)));
mp3Info.setArtist(cursor.getString(cursor
.getColumnIndex(MediaStore.Audio.Media.ARTIST)));
mp3Info.setDuration(cursor.getLong(cursor
.getColumnIndex(MediaStore.Audio.Media.DURATION)));
mp3Info.setId(cursor.getLong(cursor
.getColumnIndex(MediaStore.Audio.Media._ID)));
mp3Info.setAlbumId(cursor.getInt(cursor.getColumnIndex(MediaStore.Audio.Media.ALBUM_ID)));
mp3InfoList.add(mp3Info);
}
return mp3InfoList;
}
/**
* 格式化时间,将毫秒转换为分:秒格式//将long类型转化为String型,
* @param time
* @return
*/
public static String formatTime(long time) {
String min = time / (1000 * 60) + "";
String sec = time % (1000 * 60) + "";
if (min.length() < 2) {
min = "0" + time / (1000 * 60) + "";
} else {
min = time / (1000 * 60) + "";
}
if (sec.length() == 4) {