开发一个简易音乐播放器

本文记录了一位初学者开发Android简易音乐播放器的过程,包括实现暂停、播放、上下首切换、歌曲信息显示、播放进度控制及通知栏控制等功能。主要涉及Activity、Service、BroadcastReceiver的使用,以及SeekBar、ListView等UI组件的交互。通过MediaUtil类获取音乐信息,并在MainActivity和MusicService之间通过广播通信。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本人初学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 重要功能实现

 

1  内容提供器 ContentProvider 
  • 内容提供器用法一般有两种: 使用现有的内容提供器和创建自己的内容提供器。
  • 在使用系统自带的内容提供器,注意要在AndroidManifest中添加权限声明,如果是危险权限,还要进行运行时权限声明,这里由于本人手机低于Android6.0系统,顾没有进行运行时申请。
这里我们使用了安卓自带媒体库所提供的接口来访问数据。详情看2
 
 
2  安卓MediaStore
 
安卓为我们提供了自带的媒体库,我们可以直接从中访问到所需的数据。这个MediaStore包括了多媒体数据库的所有信息,包括音频,视频和图像,android把所有的多媒体数据库接口进行了封装,所有的数据库不用自己进行创建,直接调用利用ContentResolver去掉用那些封装好的接口就可以进行数据库的操作了,实例如下:
 
  2.1 通过genContentResolver的查询方法获得存储有数据的Cursor对象
      
//query方法第一个参数为 Uri:这个Uri代表要查询的数据库名称加上表的名称,这里我们获取所有的歌曲信息,后面四个参数为查询条件和数据排列条件,这里我们都使用null
Cursor 
cursor=context.getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, null, null)
  2.2   获得了数据结果Cursor对象后,我们读取数据到程序的思路是:通过移动光标的位置来遍历cursor的每一行,然后根据所需提取每一行相应列的信息,并添加到程序的变量中
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);
3  界面编程
 
  •  SeekBar的实现注意声明android:thumb="@drawable/media_player_progress_button"//滑动的光标
  •  按钮ImageButton背景声明我采用如下:app:srcCompat="@drawable/play"/> ,这样在活动中便可通过button.setDateResource(R.drawable....)来实现背景的切换,如果界面编程中直接使用setBackground方法,到时候会造成按钮背景的重叠而不是切 换的效果(亲测)
  •  使用ListIView的时候注意要在活动中setAdapter(),并设置响应子项点击事件(通过开启服务来播放点击的那首音乐)
 
 
4  Activity
 
  4.1  点击专辑图片时使用startActivity方法由MainActivity进入PlayActivity,并由intent传递相关信息
Intent intent=new Intent(MainActivity.this,PlayActivity.class);
                intent.putExtra("state",isPlaying);
                intent.putExtra("location",location);
                startActivity(intent);
 
  4.2 使用startService方法开启服务,通过intent传递相关信息(case 0:播放所传递的location下标那首歌
       case 1 :暂停/播放     case 2:seekBar拖动播放     case 3: 停止播放)
//传递歌曲下标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

 

7
  • 为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);
            }
        });
 
8  使用带有布局的通知 : 与使用普通通知无异,但给通知使用setContent(RemoteViews remoteViews)方法来设定布局,remoteViews的点击事件的响应为向MainActivity发送广播
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);//显示通知
 
9  实现点击按钮弹出AlertIDialog,在dialog中显示歌曲列表listView并设置点击事件,关键在于为ListView控件设置适配器:这里使用SimpleAdapter创建(要求绑定的数据是List<HashMap<String, Object>>数据类型(String为key,第二个参数为对应数据))获得dialog框架布局LinearoutLayout和歌曲列表布局ListView,并将ListIView添加到框架布局上
  •  建立一个数组存储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) {
  
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值