Android 实现简单音乐播放器(二)

Android 实现简单音乐播放器(一)中,我介绍了MusicPlayer的页面设计。

现在,我将解析MusicPlayer的功能实现,就讲一些主要的点和有趣的细节,结合MainActivity.java代码进行说明(写出来可能有点碎……一向不太会总结^·^)。

一、功能菜单

在MusicPlayer中,我添加了三个菜单:

search(搜索手机中的音乐文件,更新播放列表)、

clear(清除播放列表……这个功能是最初加进去的,后来改进之后,已经没什么实际意义)、

exit(退出)。

menu_main.xml

 1 <menu xmlns:android="http://schemas.android.com/apk/res/android"
 2     xmlns:app="http://schemas.android.com/apk/res-auto"
 3     xmlns:tools="http://schemas.android.com/tools" tools:context=".MainActivity">
 4     <item android:id="@+id/action_search" android:title="search"
 5         android:orderInCategory="100" app:showAsAction="never" />
 6     <item android:id="@+id/action_clear" android:title="clear"
 7         android:orderInCategory="100" app:showAsAction="never" />
 8     <item android:id="@+id/action_exit" android:title="exit"
 9         android:orderInCategory="100" app:showAsAction="never" />
10 </menu>

关于菜单功能,直接上代码,很简单,就不做说明啦。重要的在后面。

 1 @Override
 2     public boolean onCreateOptionsMenu(Menu menu) {
 3         // Inflate the menu; this adds items to the action bar if it is present.
 4         getMenuInflater().inflate(R.menu.menu_main, menu);
 5         return true;
 6     }
 7
 8     @Override
 9     public boolean onOptionsItemSelected(MenuItem item) {
10         // Handle action bar item clicks here. The action bar will
11         // automatically handle clicks on the Home/Up button, so long
12         // as you specify a parent activity in AndroidManifest.xml.
13         int id = item.getItemId();
14
15         //noinspection SimplifiableIfStatement
16         if (id == R.id.action_search) {
17             progressDialog=ProgressDialog.show(this,"","正在搜索音乐",true);
18             searchMusicFile();
19             return true;
20         }else if(id==R.id.action_clear){
21             list.clear();
22             listAdapter.notifyDataSetChanged();
23             return true;
24         }else if(id==R.id.action_exit){
25             flag=false;
26             mediaPlayer.stop();
27             mediaPlayer.release();
28             this.finish();
29             return true;
30         }
31         return super.onOptionsItemSelected(item);
32     }

二、搜索音乐文件——search的实现

先看一下相关的全局变量:

1 private ListView musicListView;
2 private SimpleAdapter listAdapter;
3 private List<HashMap<String,String>> list=new ArrayList<>();

为了播放音乐的便利,在播放器打开时,程序自动搜索音乐数据,将必要的信息保存在list中,并用ListView显示出来,以供用户进行选择。

而这个MusicPlayer用于播放手机外部存储设备(SD卡)的音乐,要搜索出SD卡中的全部音乐文件,主要有两种方法:1、直接遍历SD卡的File,判断文件名后缀,找到音乐文件。这种方法可以区别出一定格式的音乐文件,也可以找到对应的歌词文件,但是缺点是:遍历搜索,速度很慢。2、用Android提供的多媒体数据库MediaStore,直接用ContentResolver的query方法,就可以对MediaStore进行搜索啦,非常高效(果断选用这种方式~~),但是数据库里面没有歌词(泪目T_T~~~暂时放弃歌词播放的功能啦,以后要是想起来,再加上吧……)

 1     private void searchMusicFile(){
 2 //        如果list不是空的,就先清空
 3         if(!list.isEmpty()){
 4             list.clear();
 5         }
 6         ContentResolver contentResolver=getContentResolver();
 7         //搜索SD卡里的music文件
 8         Uri uri= MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
 9         String[] projection={
10                 MediaStore.Audio.Media._ID,      //根据_ID可以定位歌曲
11                 MediaStore.Audio.Media.TITLE,   //这个是歌曲名
12                 MediaStore.Audio.Media.DISPLAY_NAME, //这个是文件名
13                 MediaStore.Audio.Media.ARTIST,
14                 MediaStore.Audio.Media.IS_MUSIC,
15                 MediaStore.Audio.Media.DATA
16         };
17         String where=MediaStore.Audio.Media.IS_MUSIC+">0";
18         Cursor cursor=contentResolver.query(uri,projection,where,null, MediaStore.Audio.Media.DATA);
19         while (cursor.moveToNext()){
20             //将歌曲的信息保存到list中
21             //其中,TITLE和ARTIST是用来显示到ListView中的
22             // _ID和DATA都可以用来播放音乐,其实保存任一个就可以
23             String songName=cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.TITLE));
24             String artistName=cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ARTIST));
25             String id=Integer.toString(cursor.getInt(cursor.getColumnIndex(MediaStore.Audio.Media._ID)));
26             String data=Integer.toString(cursor.getInt(cursor.getColumnIndex(MediaStore.Audio.Media.DATA)));
27             HashMap<String,String> map=new HashMap<>();
28             map.put("name",songName);
29             map.put("artist",artistName);
30             map.put("id",id);
31             map.put("data",data);
32             list.add(map);
33         }
34         cursor.close();
35         //搜索完毕之后,发一个message给Handler,对ListView的显示内容进行更新
36         handler.sendEmptyMessage(SEARCH_MUSIC_SUCCESS);
37     }

搜索完了,要对ListView进行更新,这里的更新,在Handler中完成(也包括后面要讲到的播放时间的实时更新)。

 1 private Handler handler=new Handler(){
 2         @Override
 3         public void handleMessage(Message message){
 4             switch (message.what){
 5                 //更新播放列表
 6                 case SEARCH_MUSIC_SUCCESS:
 7                     listAdapter=new SimpleAdapter(MainActivity.this,list,R.layout.musiclist,
 8                             new String[]{"name","artist"}, new int[]{R.id.songName,R.id.artistName});
 9                     MainActivity.this.setListAdapter(listAdapter);
10                     Toast.makeText(MainActivity.this,"找到"+list.size()+"份音频文件",Toast.LENGTH_LONG).show();
11                     progressDialog.dismiss();
12                     break;
13                 //更新当前歌曲的播放时间
14                 case CURR_TIME_VALUE:
15                     currtimeView.setText(message.obj.toString());
16                     break;
17                 default:
18                     break;
19             }
20         }
21     };

三、选择歌曲

好了,现在我们已经有了播放列表,那么下一个步骤自然是选择要播放的歌曲咯。

我们先来看一下播放器的不同状态:

1 //    定义当前播放器的状态
2     private static final int IDLE=0;   //空闲:没有播放音乐
3     private static final int PAUSE=1;  //暂停:播放音乐时暂停
4     private static final int START=2;  //正在播放音乐

选择歌曲,在IDLE状态下才有效。选中歌曲之后,要在具有跑马灯效果的TextView中显示歌名,并且更新播放总时长。

 1     @Override
 2     protected void onListItemClick(ListView l, View v, int position, long id) {
 3         super.onListItemClick(l, v, position, id);
 4         if(currState==IDLE) {
 5 //            若在IDLE状态下,选中list中的item,则改变相应项目
 6             HashMap<String, String> map = list.get(position);
 7             nameChecked = map.get("name");
 8             Long idChecked = Long.parseLong(map.get("id"));
 9             //uriChecked:选中的歌曲相对应的Uri
10             uriChecked = Uri.parse(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI + "/" + idChecked);
11             nameView.setText(nameChecked);
12             currPosition = position; //这个是歌曲在列表中的位置,“上一曲”“下一曲”功能将会用到
13         }
14     }

四、播放

有关播放的全局变量:

1     private MediaPlayer mediaPlayer;
2     private TextView currtimeView;
3     private TextView totaltimeView;
4     private SeekBar seekBar;
5     private AlwaysMarqueeTextView nameView;
6     private ImageButton playBtn;

这里的播放,指的是音乐播放器的播放按钮,它要实现的功能有两个:1、IDLE状态下,按下即开始播放;2、播放时,按下,暂停;再按下,继续播放(这两个状态分别对应两种按钮图片)。

 1     ExecutorService executorService= Executors.newSingleThreadExecutor();
 2     public void onPlayClick(View v){
 3         switch (currState){
 4             case IDLE:
 5                 start();
 6                 currState=START;
 7                 break;
 8             case PAUSE:
 9                 mediaPlayer.start();
10                 playBtn.setImageDrawable(getResources().getDrawable(R.drawable.player_pause));
11                 currState=START;
12                 break;
13             case START:
14                 mediaPlayer.pause();
15                 playBtn.setImageDrawable(getResources().getDrawable(R.drawable.player_play));
16                 currState=PAUSE;
17                 break;
18         }
19     }
20     private void start(){
21         if(uriChecked!=null){
22             mediaPlayer.reset();
23             try {
24                 mediaPlayer.setDataSource(MainActivity.this,uriChecked);
25                 mediaPlayer.prepare();
26                 mediaPlayer.start();
27                 initSeekBar();
28                 nameView.setText(nameChecked);
29                 playBtn.setImageDrawable(getResources().getDrawable(R.drawable.player_pause));
30                 currState=START;
31                 executorService.execute(new Runnable() {
32                     @Override
33                     public void run() {
34                         flag=true;
35                         while(flag){
36                             if(mediaPlayer.getCurrentPosition()<seekBar.getMax()){
37                                 seekBar.setProgress(mediaPlayer.getCurrentPosition());
38                                 Message msg=handler.obtainMessage(CURR_TIME_VALUE,
39                                         toTime(mediaPlayer.getCurrentPosition()));
40                                 handler.sendMessage(msg);
41                                 try {
42                                     Thread.sleep(500);
43                                 } catch (InterruptedException e) {
44                                     e.printStackTrace();
45                                 }
46                             }else {
47                                 flag=false;
48                             }
49                         }
50                     }
51                 });
52             } catch (IOException e) {
53                 e.printStackTrace();
54             }
55         }else{
56             Toast.makeText(this, "播放列表为空或尚未选中曲目", Toast.LENGTH_LONG).show();
57         }
58     }

在播放时,播放进度体现在当前播放时长和进度条的变化上。因此,按下播放键时,我们要对进度条进行初始化。

1     private void initSeekBar(){
2         int duration=mediaPlayer.getDuration();
3         seekBar.setMax(duration);
4         seekBar.setProgress(0);
5         if(duration>0){
6             totaltimeView.setText(toTime(duration));
7         }
8     }

播放过程中,实时更新播放时间和进度条的工作则用一个ExecutorService来完成。

把时长(毫秒数)转化为时间格式(00:00)的方法:

1 private String toTime(int duration){
2         Date date=new Date();
3         SimpleDateFormat sdf=new SimpleDateFormat("mm:ss", Locale.getDefault());
4         sdf.setTimeZone(TimeZone.getTimeZone("GMT+0"));
5         date.setTime(duration);
6         return sdf.format(date);
7     }

五、停止

1     private void stop() {
2         initState();
3         mediaPlayer.stop();
4         currState = IDLE;
5     }

停止功能很简单,注意在停止播放时,更新必要的信息(包括按钮、状态、进度条、时间等等),我就不赘述啦

六、上一曲/下一曲

这两个功能恰好对立,实现起来原理都是一样的。这里我就只贴出上一曲的程序咯。

 1 private void previous(){
 2         if(musicListView.getCount()>0){
 3             if(currPosition>0){
 4                 switch (currState){
 5                     case IDLE:
 6                         musicListView.smoothScrollToPosition(currPosition - 1);
 7                         musicListView.performItemClick(
 8                                 musicListView.getAdapter().getView(currPosition-1,null,null),
 9                                 currPosition-1,
10                                 musicListView.getItemIdAtPosition(currPosition-1));
11                         break;
12                     case START:
13                     case PAUSE:
14                         stop();
15                         musicListView.smoothScrollToPosition(currPosition - 1);
16                         musicListView.performItemClick(
17                                 musicListView.getAdapter().getView(currPosition - 1, null, null),
18                                 currPosition - 1,
19                                 musicListView.getItemIdAtPosition(currPosition-1));
20                         break;
21                 }
22             }else{
23                 switch (currState) {
24                     case IDLE:
25                         musicListView.smoothScrollToPosition(musicListView.getCount() - 1);
26                         musicListView.performItemClick(
27                                 musicListView.getAdapter().getView(musicListView.getCount()-1, null, null),
28                                 musicListView.getCount()-1,
29                                 musicListView.getItemIdAtPosition(musicListView.getCount()-1));
30                         break;
31                     case START:
32                     case PAUSE:
33                         stop();
34                         musicListView.smoothScrollToPosition(musicListView.getCount() - 1);
35                         musicListView.performItemClick(
36                                 musicListView.getAdapter().getView(musicListView.getCount()-1, null, null),
37                                 musicListView.getCount()-1,
38                                 musicListView.getItemIdAtPosition(musicListView.getCount()-1));
39                         start();
40                         break;
41                 }
42             }
43         }
44     }

比较难的地方,就是如何在按下上一曲(或下一曲)的时候,实现出ListView的点击效果。

1  //使选中的歌曲滑动到页面显示范围内
2   musicListView.smoothScrollToPosition(currPosition - 1);
3  //单击ListView中的Item
4  musicListView.performItemClick( musicListView.getAdapter().getView(currPosition-1,null,null),currPosition-1,
5                                musicListView.getItemIdAtPosition(currPosition-1));

七、退出时,释放MediaPlayer

1     @Override
2     protected void onDestroy() {
3         if(mediaPlayer!=null){
4             mediaPlayer.stop();
5             mediaPlayer.release();
6         }
7         super.onDestroy();
8     }

八、用户权限

由于要播放SD卡中的音乐,我们还要在AndroidManifest.xml中添加读外部存储的权限。

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

好了,到现在,一个拥有基本功能的音乐播放器就完工啦。

(总算写完了~~~)

时间: 2024-08-03 06:13:23

Android 实现简单音乐播放器(二)的相关文章

Android 实现简单音乐播放器(一)

今天掐指一算,学习Android长达近两个月了,今天开始,对过去一段时间的学习收获以及遇到的疑难杂症做一些总结. 简单音乐播放器是我自己完成的第一个功能较为完整的APP,可以说是我的Android学习之路上的一个小小里程碑,给我增加了很多信心(~~真容易获得满足~~).从下面开始,我将详细介绍MusicPlayer的设计过程. 首先,先看一下这个项目的工程目录和运行效果:      从上面的图片看到,整个工程的布局文件有两个:activity_main.xml和musiclist.xml,其中,

Android开发---MediaPlayer简单音乐播放器

Android开发-MediaPlayer简单音乐播放器 功能介绍 实现一个简单的播放器,类似网易云音乐形式,功能包括: 播放.暂停,停止,退出功能: 后台播放功能: 进度条显示播放进度.拖动进度条改变进度功能: 播放时图片旋转,显示当前播放时间功能: 界面样式 功能实现 1. MediaPlayer的实现 MediaPlayer常用方法介绍 MediaPlayer的实现包括初始化MediaPlayer,MediaPlayer的功能实现,包括播放.暂停.停止.离开等,具体细节如下: MediaP

Android——简单音乐播放器

使用MediaPlayer做的简单音乐播放器,更多内容请到百度经验查看   http://jingyan.baidu.com/article/60ccbceb63452364cab197f1.html <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:l

Android Mediaplayer本地音乐播放器(绑定服务)

本文章介绍MediaPlayer本地音乐播放器,而当应用程序不再位于前台且没有正在使用它的活动时,为了确保音频继续播放,我们需要建立一个服务Service. Activity与绑定服务Service之间的交互是本文章的重点(这里需要说明一点的是,Activity不能直接访问服务对象中的方法,所以才有了我们一下的介绍,这也是为服务的安全等方面的考虑). 直接上代码: 布局文件:activity_main: <LinearLayout xmlns:android="http://schemas

Android获取第三方音乐播放器的音乐信息

最近在做Android手机获取第三方音乐播放器的音乐信息.一开始头疼的很,采集第三方的信息太难了,后面看了一遍博文是关于怎么监听系统的音乐播放信息,发现在播放下一首音乐的时候会发送广播,广播会包含下一首信息. Android系统的音乐播放器会发送: com.android.music.metachanged 那怎么获取第三方的音乐信息? 先反编译了QQ音乐的Apk, 找到它关于player的service,发现里面用的就是"com.android.music.metachaged" 接

Android开发6:Service的使用(简单音乐播放器的实现)

前言 啦啦啦~各位好久不见啦~博主最近比较忙,而且最近一次实验也是刚刚结束~ 好了不废话了,直接进入我们这次的内容~ 在这篇博文里我们将学习Service(服务)的相关知识,学会使用 Service 进行后台工作, 学会使用 Service 与 Activity 进行通信,并在此知识基础上学会使用 MediaPlayer和简单的多线程编程.使用 Handle 更新 UI,并设计成功一个简单的音乐播放器. 是不是很高大上呢~一起来学习~ 基础知识 Service作为Android四大组件之一,在每

Android开发实战之简单音乐播放器

最近开始学习音频相关.所以,很想自己做一个音乐播放器,于是,花了一天学习,将播放器的基本功能实现了出来.我觉得学习知识点还是蛮多的,所以写篇博客总结一下关于一个音乐播放器实现的逻辑.希望这篇博文对你的学习和生活有所帮助.效果图: **实现逻辑** 在市面上的音乐播放app,即时你关了.那么一样会在后台播放,所以播放的逻辑应该写在Service中.并且能够实现Service和Activity之间进行通信.那么Service是四大组件之一,所以在使用的时候一定不要忘了在配置文件中声明一下. <ser

iOS 简单音乐播放器 界面搭建

如图搭建一个音乐播放器界面,具备以下几个简单功能: 1,界面协调,整洁. 2,点击播放,控制进度条. 3.三收藏歌曲,点击收藏,心形收藏标志颜色加深. 4,左右按钮,切换歌曲图片和标题. 5,点击中间图片,隐藏所有按钮,仅显示蓝色背景. 设计的整体思路: 1.在搭建界面的时候,为了整洁和方便后续的功能的添加,需要将整个的界面划分为几个部分: ①:最上面的一行包括:一个返回按钮.一个歌曲名称.一个收藏按钮: ②:第二行:一个slider控件.两侧是当前的歌曲播放进度和歌曲的总时长--两个lable

Android开发之音乐播放器的实现

Android音乐播放器 使用到Android的Actiivity和Service组件 播放音乐使用Service组件 操作按钮等使用Activity与用户交互 同时为了把服务所在进程变成服务进程,防止Activity销毁时依旧执行Service,需要使用Service启动的混合调用,即先使用startService(),然后使用bindService()方法. 原理:使用bindService()方法,启动service.在ServiceConnection的onServiceConnecte