Android 音乐播放器 完整案例分析

来源:互联网 发布:dxo photolab for mac 编辑:程序博客网 时间:2024/04/30 18:55
 第四部分 音乐播放器
   该播放器实现了界面布局、MediaPlayer的使用、Service的使用、媒体信息的管理MediaStore等操作
   1、UI设计。通过xml文件对其进行设计
   res.layout.list_layout.xml
   <?xml version="1.0" encoding="UTF-8"?>
    <LinearLayout
     android:id="@+id/widget1"
     android:layout_width="fill_parent"
     android:layout_height="fill_parent"
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:orientation="vertical"
     android:background="@drawable/bg"
     >
     <RelativeLayout
      android:id="@+id/control_panel"
      android:orientation="horizontal"
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      >
      <TextView
       android:id="@+id/show_text"
       android:layout_width="fill_parent"
       android:layout_height="wrap_content"
       android:textSize="20sp"
       android:text="@string/click_to_play"/>
      <Button
       android:id="@+id/play_pause_btn"
       android:layout_width="100px"
       android:layout_height="wrap_content"
       android:layout_alignParentLeft="true"
       android:visibility="invisible"
       android:text="@string/play"/>        
      <Button
       android:id="@+id/stop_btn"
       android:layout_width="100px"
       android:layout_height="wrap_content"
       android:layout_alignParentRight="true"
       android:visibility="invisible"
       android:text="@string/stop"/>           
     </RelativeLayout>
     <ListView
      android:id="@id/android:list"
      android:layout_width="fill_parent"
      android:layout_height="fill_parent"
      android:cacheColorHint="#00000000"/>     
     <TextView
      android:id="@id/android:empty"
      android:layout_width="fill_parent"
      android:layout_height="fill_parent"
      android:textSize="20sp"
      android:text="@string/no_music"/>
    </LinearLayout>
    
   2、多媒体信息的管理
    为了实现在启动播放器的同时自动获取手机和SD卡上的音频文件,并显示到ListView视图中。Android系统提供了MediaScanner、MediaProvider、MediaStore等接口,及一套数据
    库,可通过ContentProvider方式提供给用户。当手机开机或有SD卡插入等事件发生时,系统将会自动扫描SD卡和手机内存上的媒体文件,如音频、视频、图片等。将相应的信息
    放到定义好的数据库表中。MediaStore中定义了一系列的数据表,通过ContentResolver提供的查询接口,可以得到各种需要的信息。如何获得这些信息及使用方法:
     ContentResolver提供了如下查询接口;
      //参数uri指明要查询的数据库名称加上表的名称;projection指定查询数据库表中的哪几列,返回的游标中将包括相应的信息,null则返回为空;
      //参数selection 指定查询条件;      参数selectionArgs 如果selection中有"?"这个符号,这里可以以实际值代替这个问号,如果没有则String数组可以为null;
      //参数sortOrder 指定查询结果的排列顺序
      Cursor query(Uri uri, String[] projection, String selection, String [] selectionArgs, String sorttOrder);
      例:
      Cursor cursor =query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null,null,null,MediaStore.Audio.Media.DEFAULT_SORT_ORDER);
      该方法将返回所以外部存储卡上的音乐文件的信息,可以通过一些常量来获得歌曲的具体信息如下:
      多媒体常用信息
       MediaStore.Audio.Media._ID     歌曲ID
       MediaStore.Audio.Media.TITLE    歌曲名称
       MediaStore.Audio.Media.ALBUM    歌曲的专辑名
       MediaStore.Audio.Media.ARTIST    歌曲歌手名
       MediaStore.Audio.Media.DATA     歌曲文件的路径
       MediaStore.Audio.Media.DURATION    歌曲的总播放时长
       MediaStore.Audio.Media.SIZE     歌曲文件的大小
       MediaStore.Audio.Playlists.DATE_MODIFIED  修改时间
       MediaStore.Audio.Playlists.DATE_ADDED  添加时间
       MediaStore.Audio.Playlists.NAME    播放列表名称
       MediaStore.Audio.Playlists._ID    播放列表ID
       MediaStore.Audio.Albums.NUMBER_OF_SONGS  该专辑共有多少歌曲
       MediaStore.Audio.Albums.ALBUM    专辑名称
       MediaStore.Audio.Albums._ID     专辑ID
       MediaStore.Audio.Artists.NUMBER_OF_TRACKS 共有多少该歌手的歌曲
       MediaStore.Audio.Artists.NUMBER_OF_ALBUMS 共有多少该歌手的专辑
       MediaStore.Audio.Artists.ARTIST    歌手姓名
       MediaStore.Audio.Artists._ID    歌手ID
      我们可以通过相应的cursor.get方法来获取这些信息。如下:
       int id = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media._ID));
       String tilte = cursor.getString(cursor.getColumIndexOrThrow(MediaStore.Audio.Media.TITLE));
       String album = cursor.getString(cursor.getColumIndexOrThrow(MediaStore.Audio.Media.ALBUM));
       String artist = cursor.getString(cursor.getColumIndexOrThrow(MediaStore.Audio.Media.ARTIST));
       int size = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.SIZE));
       int duration = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DURATION));
      
       Cursor cursor =query(MediaStore.Audio.Artists.EXTERNAL_CONTENT_URI, null,null,null,MediaStore.Audio.Artists.DEFAULT_SORT_ORDER);//查询歌手信息,返回所有外部存储卡上的歌手信息
       Cursor cursor =query(MeidaStore.Audio.Albums.EXTERNAL_CONTENT_URI, null,null,null,MediaStore.Audio.Albums.DEFAULT_SORT_ORDER);//查询专辑,返回所有外部存储卡上的专辑信息
       Cursor cursor =query(MeidaStore.Audio.Playlists.EXTERNAL_CONTENT_URI,null,null,null,MeidaStore.Audio.Playlists.DEFAULT_SORT_ORDER);//查询播放列表,返回所有外部存储卡上的专辑信息
      通过这些查询结果,指定查询条件,用户可以很方便的查询指定的媒体信息,如:
       query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, MediaStroe.Audio.Media.ARTIST_ID+"="+aid, null, MediaStore.Audio.Media.TITLE);//查询属于指定歌手(歌手ID为"aid")的歌曲
       query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null,MediaStore.Audio.Media.ALBUM_ID+"="+ aid, null,MediaStore.Audio.Media.TITLE);//查询属于指定专辑(歌手ID为"aid")的歌曲
  
   public class MusicInfoController{//定义一个用来管理音乐信息的类
    private static MusicInfoController mInstance = null;
    private MusicPlayerApp     pApp  = null;
    public static MusicInfoController getInstance(MusicPlayerApp app){
     if (mInstance == null){
      mInstance = new MusicInfoController(app);
     }
     return mInstance;
    }
    private MusicInfoController(MusicPlayerApp app){
     pApp = app;
    }
    public MusicPlayerApp getMusicPlayer(){
     return pApp;
    }
    private Cursor query(Uri uri, String[] prjs, String selections, String[] selectArgs, String order){//查询指定信息
     ContentResolver resolver = pApp.getContentResolver();
     if (resolver == null){
      return null;
     }
     return resolver.query(uri, prjs, selections, selectArgs, order);
    }
    public Cursor getAllSongs(){//查询所有歌曲信息
     return query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, null, null, MediaStore.Audio.Media.DEFAULT_SORT_ORDER);
    }
   }
   
   3、播放音乐  
   public class MusicPlayerService extends Service{//我们希望当退出程序界面后,音乐仍然继续播放这就需要Service,本例采用了Local Service它与程序运行在同一个进程中。
    private final IBinder mBinder = new LocalBinder();  
    private MediaPlayer mMediaPlayer = null;  //音乐文件的播放是由MediaPlayer类实现的,如常用接口播放、暂停、停止、快速定位等    
    public static final String PLAYER_PREPARE_END = "com.yarin.musicplayerservice.prepared";
    public static final String PLAY_COMPLETED = "com.yarin.musicplayerservice.playcompleted";
      
    MediaPlayer.OnCompletionListener mCompleteListener = new MediaPlayer.OnCompletionListener() {//给MediaPlay添加MediaPlayer.OnPreparedListener和
                            MediaPlay.OnCompletionListener监听准备完毕和播放结束的消息
     public void onCompletion(MediaPlayer mp) {
      broadcastEvent(PLAY_COMPLETED);
     }
    };
    MediaPlayer.OnPreparedListener mPrepareListener = new MediaPlayer.OnPreparedListener() {
     public void onPrepared(MediaPlayer mp) {  
      broadcastEvent(PLAYER_PREPARE_END);
     }
    };     
    private void broadcastEvent(String what){//在我们没有播放歌曲时,播放,暂停等按钮处于不可见状态,当选择其中一首时,
               要显示这些按钮,当这个状态改变时,通过Intent的形式,将准备完毕或播放等消息广播(BroadCastReceiver)出去。
     Intent i = new Intent(what);
     sendBroadcast(i);
    }
    public void onCreate(){
     super.onCreate();
     mMediaPlayer = new MediaPlayer();//对MediaPlayer用来播放音乐,在此处初始化
     mMediaPlayer.setOnPreparedListener(mPrepareListener);
     mMediaPlayer.setOnCompletionListener(mCompleteListener);
    }
    public class LocalBinder extends Binder{
     public MusicPlayerService getService(){//通过LocalBinder的getService方法得当MusicPlayerService的实例
      return MusicPlayerService.this;
     }
    }
    public IBinder onBind(Intent intent){//重置 onBind 方法,返回自定义的 LocalBinder
     return mBinder;
    }
    public void setDataSource(String path){
     try{
      mMediaPlayer.reset();
      mMediaPlayer.setDataSource(path);
      mMediaPlayer.prepare();
     }catch (IOException e){
      return;
     }catch (IllegalArgumentException e){
      return;
     }
    }
    public void start(){//播放
     mMediaPlayer.start();
    }
    public void stop(){//停止
     mMediaPlayer.stop();
    }
    public void pause(){//暂停
     mMediaPlayer.pause();
    }
    public boolean isPlaying(){
     return mMediaPlayer.isPlaying();
    }
    public int getDuration(){
     return mMediaPlayer.getDuration();
    }
    public int getPosition(){
     return mMediaPlayer.getCurrentPosition();
    }
    public long seek(long whereto){
     mMediaPlayer.seekTo((int) whereto);
     return whereto;
    }
   }

   public class MusicList extends ListActivity{//通过MusicList来显示播放列表开启服务Service,在后台播放音乐.
    private MusicPlayerService mMusicPlayerService = null;
    private MusicInfoController mMusicInfoController = null;
    private Cursor mCursor = null;  
    private TextView mTextView = null;
    private Button mPlayPauseButton = null;
    private Button mStopButton = null;
    private ServiceConnection mPlaybackConnection = new ServiceConnection() {
     public void onServiceConnected(ComponentName className, IBinder service) { 
      mMusicPlayerService = ((MusicPlayerService.LocalBinder)service).getService();
     }
     public void onServiceDisconnected(ComponentName className) {
      mMusicPlayerService = null;
     }
    };
    protected BroadcastReceiver mPlayerEvtReceiver = new BroadcastReceiver() {//对接收到广播进行处理
     public void onReceive(Context context, Intent intent) {
      String action = intent.getAction();
      if (action.equals(MusicPlayerService.PLAYER_PREPARE_END)) {
       // will begin to play
       mTextView.setVisibility(View.INVISIBLE);
       mPlayPauseButton.setVisibility(View.VISIBLE);
       mStopButton.setVisibility(View.VISIBLE);
       mPlayPauseButton.setText(R.string.pause);
      } else if(action.equals(MusicPlayerService.PLAY_COMPLETED)) {
       mPlayPauseButton.setText(R.string.play);
      }
     }
    };
    public void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     setContentView(R.layout.list_layout);
       
     MusicPlayerApp musicPlayerApp=(MusicPlayerApp)getApplication();
     mMusicInfoController = (musicPlayerApp).getMusicInfoController();      
     startService(new Intent(this,MusicPlayerService.class));// bind playback service       
     bindService(new Intent(this,MusicPlayerService.class), mPlaybackConnection, Context.BIND_AUTO_CREATE);
         
     mTextView = (TextView)findViewById(R.id.show_text);
     mPlayPauseButton = (Button) findViewById(R.id.play_pause_btn);
     mStopButton = (Button) findViewById(R.id.stop_btn);
       
     mPlayPauseButton.setOnClickListener(new Button.OnClickListener() {
      public void onClick(View v) {
       if (mMusicPlayerService != null && mMusicPlayerService.isPlaying()) {// Perform action on click
        mMusicPlayerService.pause();
        mPlayPauseButton.setText(R.string.play);
       } else if (mMusicPlayerService != null){
        mMusicPlayerService.start();
        mPlayPauseButton.setText(R.string.pause);
       }
      }
     });
     mStopButton.setOnClickListener(new Button.OnClickListener() {
      public void onClick(View v) {
       if (mMusicPlayerService != null ) { // Perform action on click
        mTextView.setVisibility(View.VISIBLE);
        mPlayPauseButton.setVisibility(View.INVISIBLE);
        mStopButton.setVisibility(View.INVISIBLE);
        mMusicPlayerService.stop();
       }
      }
     });
     IntentFilter filter = new IntentFilter();
     filter.addAction(MusicPlayerService.PLAYER_PREPARE_END);
     filter.addAction(MusicPlayerService.PLAY_COMPLETED);
     registerReceiver(mPlayerEvtReceiver, filter);
    }
    protected void onResume() {
     super.onResume();
     mCursor = mMusicInfoController.getAllSongs();
     ListAdapter adapter = new MusicListAdapter(this, android.R.layout.simple_expandable_list_item_2, mCursor, new String[]{}, new int[]{});
     setListAdapter(adapter);
    }
    protected void onListItemClick(ListView l, View v, int position, long id) {
     super.onListItemClick(l, v, position, id);
     if (mCursor == null ||mCursor.getCount() == 0) {
      return;
     }
     mCursor.moveToPosition(position);
     String url = mCursor.getString(mCursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA));
     mMusicPlayerService.setDataSource(url);
     mMusicPlayerService.start();
    }
   }

   class MusicListAdapter extends SimpleCursorAdapter {
    public MusicListAdapter(Context context, int layout, Cursor c,
     String[] from, int[] to) {
      super(context, layout, c, from, to);
     } 
    public void bindView(View view, Context context, Cursor cursor) {   
     super.bindView(view, context, cursor); 
     TextView titleView = (TextView) view.findViewById(android.R.id.text1);
     TextView artistView = (TextView) view.findViewById(android.R.id.text2);
     titleView.setText(cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE)));
     artistView.setText(cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST)));
     //int duration = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DURATION));  
    }
    public static String makeTimeString(long milliSecs) {
     StringBuffer sb = new StringBuffer();
     long m = milliSecs / (60 * 1000);
     sb.append(m < 10 ? "0" + m : m);
     sb.append(":");
     long s = (milliSecs % (60 * 1000)) / 1000;
     sb.append(s < 10 ? "0" + s : s);
     return sb.toString();
    }
   }
 
   public class MusicPlayerApp extends Application{
    private MusicInfoController mMusicInfoController = null;
    public void onCreate(){
     super.onCreate();
     mMusicInfoController = MusicInfoController.getInstance(this);
    }
    public MusicInfoController getMusicInfoController(){
     return mMusicInfoController;
    }
   }
   
   AndroidManifest.xml
   <?xml version="1.0" encoding="utf-8"?>
    <manifest
     xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.yarin.android.MusicPlayer"
     android:versionCode="1"
     android:versionName="1.0">
     <application
      android:name="MusicPlayerApp"
      android:icon="@drawable/icon"
      android:label="@string/app_name">
      <activity
       android:name=".MusicList">
       <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
       </intent-filter>
      </activity>
      <service //用于使用了Service来播放音乐,所有需要注册权限,另外程序中MusicInfoController采用单例模式,使程序只要唯一的实例,我们传入的MusicPlayerApp
         作为Context生成ContentResolver,用来查询媒体库。
       android:name=".MusicPlayerService"
       android:exported="true" > 
      </service>
     </application>
     <uses-sdk android:minSdkVersion="5" />
    </manifest>

原创粉丝点击