在Android 实现简单音乐播放器(一)中,我介绍了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>
View Code

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

 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 will11         // automatically handle clicks on the Home/Up button, so long12         // as you specify a parent activity in AndroidManifest.xml.13         int id = item.getItemId();14 15         //noinspection SimplifiableIfStatement16         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     }
View Code

二、搜索音乐文件——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.DATA16         };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:选中的歌曲相对应的Uri10             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                     @Override33                     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     }
View Code

五、停止

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     }
View Code

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

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

七、退出时,释放MediaPlayer

1     @Override2     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"/>

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

(总算写完了~~~)

更多相关文章

  1. Android热更新框架Nuwa的使用
  2. 下载最新Android代码的方法
  3. android-如何在子线程中更新ui
  4. 慎用原生MediaPlayer类播放音频
  5. Android热更新之AndFix就是个大坑
  6. 一些好的博客收集(持续更新中)
  7. Android集成Bugly热更新
  8. Android软件自动更新升级(自动下载安装新版本)
  9. Android实现简单的音乐播放

随机推荐

  1. Android(安卓)Toast 例子
  2. android设备计算密度,宽高!
  3. Failed to install the following Androi
  4. android 实现图片加水印
  5. android studio Unknown host 切换 阿里
  6. android控件根据屏幕大小自适应
  7. android 下载编译环境配置简要
  8. 24-Android监听精确的通话状态
  9. android篇-如何做一个简单的安卓源码查看
  10. 快捷方式