zoukankan      html  css  js  c++  java
  • Android音乐播放器的开发实例

    本文将引导大家做一个音乐播放器,在做这个Android开发实例的过程中,能够帮助大家进一步熟悉和掌握学过的ListView和其他一些组件。为了有更好的学习效果,其中很多功能我们手动实现,例如音乐播放的快进快退等。

           先欣赏下本实例完成后运行的界面效果:

    Android音乐播放器的开发实例

            首先我们建立项目,我使用的SDK是Android2.2的,然后在XML中进行布局。

           上方是一个ListView用来显示我们的音乐列表,中间是一个SeekBar可以拖动当前音乐的播放进度,之所以用SeekBar而不用ProgressBar是因为我们需要音乐的快进快退功能,可以拖动滑杆改变进度;还有一个TextView,用来显示当前播放歌曲的名字,时长等。最下方就是4个Button了,分别是上一曲,播放(暂停),停止,下一曲。

           大家注意尽量不要在布局中出现直接显示在界面上的文字内容,我们把这些内容都放在res/values下的strings.xml中,然后分别引用它们,这样养成良好的习惯,界面与内容分离,方便调试和后期维护等。现在我们的界面如下:

    Android音乐播放器XML布局

           然后我们把File Explorer打开,在eclipse的Window -- Show View -- Other --Android --File Explore。你也可以直接Alt+Shift+Q。

    File Explorer

           在mnt/sdcard下面,我们放个两三首歌曲,在虚拟机中暂不支持中文,导入有中文的文件会报错的。

           接着我们创建一个类,做我们播放器的Service类,我就叫MusicService吧,在里面声明以下对象:

    Java代码
    1. public class MusicService {   
    2.   
    3.     private static final File MUSIC_PATH = Environment   
    4.             .getExternalStorageDirectory();// 找到music存放的路径。   
    5.     public List<String> musicList;// 存放找到的所有mp3的绝对路径。   
    6.     public MediaPlayer player; // 定义多媒体对象   
    7.     public int songNum; // 当前播放的歌曲在List中的下标   
    8.     public String songName; // 当前播放的歌曲名   
    9.   
    10. }  

           然后我们去加载刚才添加的MP3文件吧,这里的方式多种多样,我随便写一个简单的了:

    Java代码
    1. class MusicFilter implements FilenameFilter {   
    2.      public boolean accept(File dir, String name) {   
    3.      return (name.endsWith(".mp3"));//返回当前目录所有以.mp3结尾的文件   
    4.      }   
    5. }  

           在MusicService类的无参构造函数中实例化对象,并把这些MP3文件放到musicList中。

    Java代码
    1. public MusicService() {   
    2.     musicList = new ArrayList<String>();   
    3.     player = new MediaPlayer();   
    4.   
    5.     if (MUSIC_PATH.listFiles(new MusicFilter()).length > 0) {   
    6.         for (File file : MUSIC_PATH.listFiles(new MusicFilter())) {   
    7.             musicList.add(file.getAbsolutePath());   
    8.         }   
    9.     }   
    10. }  

           我们写个方法,来设置当前播放歌曲的名字:(个人觉得这方法比较笨,但暂时没想到别的办法)

    Java代码
    1. public void setPlayName(String dataSource) {   
    2.     File file = new File(dataSource);//假设为D:\mm.mp3   
    3.     String name = file.getName();//name=mm.mp3   
    4.     int index = name.lastIndexOf(".");//找到最后一个.   
    5.     songName = name.substring(0, index);//截取为mm   
    6. }  

          接下来就是我们Service类的基本方法了,也就是开始、暂停、停止、上一首和下一首。

          我们分别使用声明的多媒体对象的start、pause、stop等方法可以完成。

    Java代码
    1. public void start() {   
    2.     try {   
    3.         player.reset(); //重置多媒体   
    4.         String dataSource = musicList.get(songNum);//得到当前播放音乐的路径   
    5.         setPlayName(dataSource);//截取歌名   
    6.         player.setDataSource(dataSource);//为多媒体对象设置播放路径   
    7.         player.prepare();//准备播放   
    8.         player.start();//开始播放   
    9.         //setOnCompletionListener 当当前多媒体对象播放完成时发生的事件   
    10.         player.setOnCompletionListener(new OnCompletionListener() {   
    11.             public void onCompletion(MediaPlayer arg0) {   
    12.                 next();//如果当前歌曲播放完毕,自动播放下一首.   
    13.             }   
    14.         });   
    15.     } catch (Exception e) {   
    16.         Log.v("MusicService", e.getMessage());   
    17.     }   
    18. }   
    19.   
    20. public void next() {   
    21.     songNum = songNum == musicList.size() - 1 ? 0 : songNum + 1;   
    22.     start();   
    23. }   
    24.   
    25. public void last() {   
    26.     songNum = songNum == 0 ? musicList.size() - 1 : songNum - 1;   
    27.     start();   
    28. }   
    29.   
    30. public void pause() {   
    31.     if (player.isPlaying())   
    32.         player.pause();   
    33.     else  
    34.         player.start();   
    35. }   
    36.   
    37. public void stop() {   
    38.     if (player.isPlaying()) {   
    39.         player.stop();   
    40.     }   
    41. }  

           到此为止我们的Service类就写完了,接着我们去Activity中为各控件绑定事件。

           在这个Activity中,最难做的一点应该就是拖动SeekBar的滑杆改变播放进度了,这里我考虑再三,用了一个Handler类来处理。

           Handler在android里负责发送和处理消息。它的主要用途有:

           1.按计划发送消息或执行某个Runnanble(使用POST方法)。

           2.从其他线程中发送来的消息放入消息队列中,避免线程冲突(常见于更新UI线程)。

           默认情况下,Handler接受的是当前线程下的消息循环实例(使用Handler(Looper looper)、Handler(Looper looper, Handler.Callback callback)可以指定线程),同时一个消息队列可以被当前线程中的多个对象进行分发、处理(在UI线程中,系统已经有一个Activity来处理了,你可以再起若干个Handler来处理)。在实例化Handler的时候,Looper可以是任意线程的,只要有Handler的指针,任何线程也都可以sendMessage。Handler对于Message的处理不是并发的。一个Looper 只有处理完一条Message才会读取下一条,所以消息的处理是阻塞形式的(handleMessage()方法里不应该有耗时操作,可以将耗时操作放在其他线程执行,操作完后发送Message(通过sendMessges方法),然后由handleMessage()更新UI)。

           声明以下变量:

    Java代码
    1. private Button btnStart, btnStop, btnNext, btnLast;   
    2. private TextView txtInfo;   
    3. private ListView listView;   
    4. private SeekBar seekBar;   
    5. private MusicService musicService;   
    6. private MusicHandler musicHandler;// 处理改变进度条事件   
    7. private MusicThread musicThread;// 自动改变进度条的线程   
    8. private boolean autoChange, manulChange;// 判断是进度条是自动改变还是手动改变   
    9. private boolean isPause;// 判断是从暂停中恢复还是重新播放  

           如有报错的可以先注释掉不用管它,然后在初始化过程中绑定事件。

           这是ListView的填充方法:

    Java代码
    1. private void setListViewAdapter() {   
    2.     List<Map<String, Object>> date = new ArrayList<Map<String, Object>>();   
    3.   
    4.     for (String path : musicService.musicList) {   
    5.         Map<String, Object> map = new HashMap<String, Object>();   
    6.         File file = new File(path);   
    7.         map.put("fileName", file.getName());   
    8.         date.add(map);   
    9.     }   
    10.     SimpleAdapter adapter = new SimpleAdapter(this, date,   
    11.                 android.R.layout.simple_list_item_1,   
    12.                 new String[] { "fileName" }, new int[] { android.R.id.text1 });   
    13.   
    14.     listView.setAdapter(adapter);   
    15.   
    16. }  

           SimpleAdapter的构造函数是:

           public SimpleAdapter (Context context, List<? extends Map<String, ?>> data, int resource, String[] from, int[] to);

           第一个参数context,是指在哪个Activity中显示。

           第二个参数是一个泛型作为数据源,而且每一个List中的一行就代表着呈现出来的一行,Map的键就是这一行的列名,值也是有列名的。

           第三个参数为资源文件,就是说要加载这个列所需要的视图资源文件,我直接引用系统内置的资源,如果你想要漂亮的样式可以自己写的。

           第四个参数是一个String数组,主要是将Map对象中的名称映射到列名,一一对应。

           第五个是将第四个参数的值一一对象的显示(一一对应)在接下来的int形的id数组中,这个id数组就是Layout的xml文件中命名id形成的唯一的int型标识符。

           SeekBar停止拖动后的事件:

    Java代码
    1. public void onStopTrackingTouch(SeekBar seekBar) { // 停止拖动      
    2.     int progress = seekBar.getProgress();      
    3.      
    4.     if (!autoChange && manulChange) {      
    5.         int musicMax = musicService.player.getDuration(); //得到该首歌曲最长秒数      
    6.         int seekBarMax = seekBar.getMax();      
    7.      
    8.         musicService.player      
    9.                 .seekTo(musicMax * progress / seekBarMax);//跳到该曲该秒                     
    10.     musicService.pause();      
    11.     autoChange = true;      
    12.     manulChange = false;      
    13.     }      
    14. }    

           MusicHandler类的实现:

    Java代码
    1. class MusicHandler extends Handler {   
    2.   
    3.          public MusicHandler() {   
    4.     }   
    5.   
    6.     @Override  
    7.     public void handleMessage(Message msg) {   
    8.         if (autoChange) {   
    9.             try {   
    10.                 int position = musicService.player.getCurrentPosition();//得到当前歌曲播放进度(秒)   
    11.                 int mMax = musicService.player.getDuration();//最大秒数   
    12.                 int sMax = seekBar.getMax();//seekBar最大值,算百分比   
    13.                     seekBar.setProgress(position * sMax / mMax);   
    14.                     txtInfo.setText(setPlayInfo(position / 1000, mMax / 1000));   
    15.             } catch (Exception e) {   
    16.                     e.printStackTrace();   
    17.             }   
    18.         } else {   
    19.             seekBar.setProgress(0);   
    20.             txtInfo.setText("播放已经停止");   
    21.         }   
    22.     }   
    23. }   
    24.   
    25. //设置当前播放的信息   
    26. private String setPlayInfo(int position, int max) {   
    27.     String info = "正在播放:  " + musicService.songName + " ";   
    28.   
    29.     //笨办法 写完才想起可以用%的,但不想改了   
    30.     int pMinutes = 0;   
    31.     while (position >= 60) {   
    32.         pMinutes++;   
    33.         position -= 60;   
    34.     }   
    35.     String now = (pMinutes < 10 ? "0" + pMinutes : pMinutes) + ":"  
    36.         + (position < 10 ? "0" + position : position);   
    37.   
    38.     int mMinutes = 0;   
    39.     while (max >= 60) {   
    40.         mMinutes++;   
    41.         max -= 60;   
    42.     }   
    43.     String all = (mMinutes < 10 ? "0" + mMinutes : mMinutes) + ":"  
    44.         + (max < 10 ? "0" + max : max);   
    45.   
    46.     return info + now + " / " + all;   
    47. }  

           MusicThread的实现:

    Java代码
    1. class MusicThread implements Runnable {   
    2.   
    3.     @Override  
    4.     public void run() {   
    5.         while (true)   
    6.             try {   
    7.                     musicHandler.sendMessage(new Message());   
    8.                 Thread.sleep(1000);// 每间隔1秒发送一次更新消息   
    9.             } catch (InterruptedException e) {   
    10.                     e.printStackTrace();   
    11.             }   
    12.     }   
    13.   
    14. }  
  • 相关阅读:
    398. Random Pick Index
    382. Linked List Random Node
    645. Set Mismatch
    174. Dungeon Game
    264. Ugly Number II
    115. Distinct Subsequences
    372. Super Pow
    LeetCode 242 有效的字母异位词
    LeetCode 78 子集
    LeetCode 404 左叶子之和
  • 原文地址:https://www.cnblogs.com/mafeng/p/5269467.html
Copyright © 2011-2022 走看看