我们用了6篇文章的篇幅做了铺垫,终于到了真正的应用程序了。这部分还是一如既往的简单。

有关应用的类有两个,一个是LiryicMain,一个是SelectFileActivity。都是差不多最低限度的内容,没有任何华丽的内容。

先看看这两个类在整个软件中的位置。从图中可以看出LyricMain是软件全体的控制者。SelectFileActivity也为LyricMain提供服务。

SelectFileActivity太过简单,本文中就不再说明了。我们集中篇幅说明一下LyricMain。

首先是数据成员。一个是LyricPlayerServiceProxy,歌词播放服务的代理,一个是用来保存歌词结束位置的List。

 
  
  1. private LyricPlayerServiceProxy mProxy = new LyricPlayerServiceProxy(this);  
  2. private ArrayList<Integer> mLyricEndList = new ArrayList<Integer>(); 

LyricPlayerServiceProxy是前面已经介绍过的内容,在这里就不在重复了。mLyricEndList需要说明一下。在这个软件中我们将所有歌词都表示在一个TextEditView中,为了能够表示当前播放中的歌词,我们将每一句歌词的位置保存在mLyricEndList中,这样当播放中的歌词发生变化时,只要将这句歌词设为选中状态就可以了。

 接下来是LyricMediaInfoProvider的最简单实现,提供了固定的歌名和歌曲文件的位置信息。如果需要切换歌曲,需要再复杂一些。

 
  
  1. private class LyricMediaInfoProvider implements MediaPlayerService.MediaInfoProvider{  
  2.     String mUrl;  
  3.     String mTitle;  
  4.       
  5.     LyricMediaInfoProvider(String url, String title){  
  6.         mUrl = url;  
  7.         mTitle = title;  
  8.     }  
  9.       
  10.     @Override 
  11.     public boolean moveToPrev() {  
  12.         // TODO Auto-generated method stub  
  13.         return false;  
  14.     }  
  15.       
  16.     @Override 
  17.     public boolean moveToNext() {  
  18.         // TODO Auto-generated method stub  
  19.         return false;  
  20.     }  
  21.       
  22.     @Override 
  23.     public String getUrl() {  
  24.         return mUrl;  
  25.     }  
  26.       
  27.     @Override 
  28.     public String getTitle() {  
  29.         // TODO Auto-generated method stub  
  30.         return mTitle;  
  31.     }  

接下来是onCreate方法。主要做了几件事

1.建立和LyricPlayerServiceProxy之间的联系。

2.提供了的实现NotificationProvider(详细信息请参照: Android歌词秀设计思路(4)通用的音乐播放服务(下)

3.设置ImageButton的尺寸。

 
  
  1. /** Called when the activity is first created. */ 
  2.    @Override 
  3.    public void onCreate(Bundle savedInstanceState) {  
  4.        super.onCreate(savedInstanceState);  
  5.        setContentView(R.layout.main);  
  6.          
  7.        mLyricEdit = (EditText)this.findViewById(R.id.editLyric);  
  8.          
  9.        mProxy.setConnectionListener(this);  
  10.        mProxy.setLyricPlayerListener(this);  
  11.        mProxy.setNotificationProvider(new MediaPlayerService.NotificationProvider(){  
  12.         @Override 
  13.         public Notification createNotification(Context context) {  
  14.             Notification notification = new Notification(R.drawable.button_blue_play, mProxy.getTitle(), System.currentTimeMillis());  
  15.             // The PendingIntent to launch our activity if the user selects this notification  
  16.             PendingIntent contentIntent = PendingIntent.getActivity(context, 0new Intent(context, LyricMain.class), 0);   
  17.             // Set the info for the views that show in the notification panel.  
  18.             notification.setLatestEventInfo(context, getText(R.string.media_player_label), mProxy.getTitle(), contentIntent);  
  19.             return notification;  
  20.         }  
  21.     });  
  22.        mProxy.startAndBindService();  
  23.        mLyricEndList.clear();  
  24.          
  25.        DisplayMetrics metrics = new DisplayMetrics();  
  26.        getWindowManager().getDefaultDisplay().getMetrics(metrics);  
  27.                  
  28.        int btnId[] = {R.id.buttonPrev, R.id.buttonStop, R.id.buttonPlay, R.id.buttonPause, R.id.buttonNext};  
  29.        int btnSize = Math.min(metrics.widthPixels, metrics.heightPixels) / (btnId.length + 1);  
  30.          
  31.        //调整按键尺寸。  
  32.        for(int i = 0; i < btnId.length; ++i){  
  33.         ImageButton ib = (ImageButton)this.findViewById(btnId[i]);  
  34.         ib.setAdjustViewBounds(true);  
  35.         ib.setMaxHeight(btnSize);  
  36.         ib.setMaxWidth(btnSize);  
  37.        }  
  38.        ImageButton selectFile = (ImageButton)this.findViewById(R.id.buttonSelectFile);  
  39.        selectFile.setAdjustViewBounds(true);  
  40.        selectFile.setMaxHeight(btnSize*2/3);  
  41.        selectFile.setMaxWidth(btnSize*2/3);  
  42.       
  43.        updateButtonState();  
  44.    } 

再下来是onDestroy方法,如果音乐在播放中,就接触和播放服务之间的关系,退出程序,这是歌曲播放会继续。如果播放出于停止或暂停状态,就连同播放服务一起关闭,完全退出程序。

 
  
  1. @Override 
  2.     protected void onDestroy() {  
  3.         super.onDestroy();  
  4.         mProxy.setConnectionListener(null);  
  5.         mProxy.setLyricPlayerListener(null);  
  6.         if(!mProxy.isPlaying()){  
  7.             mProxy.stopService();  
  8.         }  
  9.     } 

启动选择文件的SelectFileActivity

 
  
  1. public void OnSelectFile(View v){  
  2.         Intent i = new Intent(this, SelectFileActivity.class);  
  3.         startActivityForResult(i, 0);  
  4.     } 

SelectFileActivity关闭,取得选中的媒体文件的信息并通知的LyricPlayerServiceProxy

接下来是按键处理 

 
  
  1. public void OnOperationButtonClick(View v){  
  2.         switch(v.getId()){  
  3.         case R.id.buttonPrev:  
  4.             mProxy.seekToPrevLyric();  
  5.             break;  
  6.         case R.id.buttonStop:  
  7.             if(mProxy.isPlaying() || mProxy.isPausing()){  
  8.                 mProxy.stop();  
  9.             }  
  10.             break;  
  11.         case R.id.buttonPlay:  
  12.             if(!mProxy.isPlaying()){  
  13.                 mProxy.start();  
  14.             }  
  15.             break;  
  16.         case R.id.buttonPause:  
  17.             if(mProxy.isPlaying()){  
  18.                 mProxy.pause();  
  19.             }  
  20.             break;  
  21.         case R.id.buttonNext:  
  22.             mProxy.seekToNextLyric();  
  23.             break;  
  24.         }  
  25.      } 

根据播放状态更新各个按键的状态。

 
  
  1. protected void updateButtonState(){  
  2.         ((ImageButton)this.findViewById(R.id.buttonPrev)).setEnabled(mProxy.isPlaying() || mProxy.isPausing());  
  3.         ((ImageButton)this.findViewById(R.id.buttonStop)).setEnabled(mProxy.isPlaying() || mProxy.isPausing());  
  4.         ((ImageButton)this.findViewById(R.id.buttonPlay)).setEnabled(mProxy.getDataSource()!= null && (!mProxy.isPlaying() || mProxy.isPausing()));  
  5.         ((ImageButton)this.findViewById(R.id.buttonPause)).setEnabled(mProxy.isPlaying());  
  6.         ((ImageButton)this.findViewById(R.id.buttonNext)).setEnabled(mProxy.isPlaying() || mProxy.isPausing());  
  7.     } 

如果是程序启动时已经有歌曲在播放,就更新一下文件标题和按钮状态。

 
  
  1. //implement of LyricPlayerServiceProxy.ServiceConnectionListener  
  2.     public void onServiceConnected(){  
  3.         String title = mProxy.getTitle();  
  4.         if(title != null){  
  5.             TextView tv = (TextView)this.findViewById(R.id.fileTitle);  
  6.             tv.setText(title);  
  7.         }  
  8.         updateButtonState();  
  9.     }  
  10.     public void onServiceDisconnected(){  
  11.           
  12.     } 

实现LyricPlayerListener的代码,负责处理歌词播放服务的各种通知。

 
  
  1. //implement of LyricPlayerService.LyricPlayerListener  
  2.     public void onLyricLoaded(){  
  3.         mLyricEndList.clear();  
  4.         String lyric = new String();  
  5.         for(int i = 0; i < mProxy.getLyricCount(); ++i){  
  6.             lyric += mProxy.getLyric(i);  
  7.             lyric += "\r\n";  
  8.             mLyricEndList.add(new Integer(lyric.length()));  
  9.         }  
  10.         mLyricEdit.setText(lyric);  
  11.      }  
  12.       
  13.     public void onStateChanged(){  
  14.         updateButtonState();  
  15.     }  
  16.       
  17.     public void onPositionChanged(long position){  
  18.           
  19.     }  
  20.       
  21.     public void onLyricChanged(int lyric_index){  
  22.         int lyricStart = 0;  
  23.         if(lyric_index > 0){  
  24.             lyricStart = mLyricEndList.get(lyric_index - 1);  
  25.         }  
  26.         int lyricEnd = mLyricEndList.get(lyric_index);  
  27.         mLyricEdit.setSelection(lyricStart, lyricEnd);  
  28.         mLyricEdit.invalidate();  
  29.         Log.i(TAG, String.format("lyric= %d, setSelection(%d, %d)", lyric_index, lyricStart, lyricEnd));  
  30.     } 

在歌词读入时,将所有歌词练成一个长字符串,并记住每一句歌词在字符串中的位置。

在播放服务的状态发生变化时,更新按钮的状态。

在当前歌词发生变化时,根据前面保存的位置信息将当前歌词设置成高亮。

最后是跳到选定歌词的代码,还是一样的简单。

 
  
  1. public void OnLyricClick(View v){  
  2.         EditText et = (EditText)v;  
  3.         int sel_start = et.getSelectionStart();  
  4.         for(int i = 0; i < mLyricEndList.size(); ++i){  
  5.             if(sel_start < mLyricEndList.get(i))  
  6.             {  
  7.                 mProxy.seekToLyric(i);  
  8.                 break;  
  9.             }  
  10.         }  
  11.     } 

结合选中的位置,和保存的歌词位置信息,找到歌词的序号,让播放服务跳到那句就行了。