1. 程式人生 > 實用技巧 >Android歌詞秀設計思路(7)水到渠成

Android歌詞秀設計思路(7)水到渠成

我們用了6篇文章的篇幅做了鋪墊,終於到了真正的應用程式了。這部分還是一如既往的簡單。

有關應用的類有兩個,一個是LiryicMain,一個是SelectFileActivity。都是差不多最低限度的內容,沒有任何華麗的內容。

先看看這兩個類在整個軟體中的位置。從圖中可以看出LyricMain是軟體全體的控制者。SelectFileActivity也為LyricMain提供服務。

SelectFileActivity太過簡單,本文中就不再說明了。我們集中篇幅說明一下LyricMain。

首先是資料成員。一個是LyricPlayerServiceProxy,歌詞播放服務的代理,一個是用來儲存歌詞結束位置的List。

  1. privateLyricPlayerServiceProxymProxy=newLyricPlayerServiceProxy(this);
  2. private ArrayList<Integer>mLyricEndList=newArrayList<Integer>();

LyricPlayerServiceProxy是前面已經介紹過的內容,在這裡就不在重複了。mLyricEndList需要說明一下。在這個軟體中我們將所有歌詞都表示在一個TextEditView中,為了能夠表示當前播放中的歌詞,我們將每一句歌詞的位置儲存在mLyricEndList中,這樣當播放中的歌詞發生變化時,只要將這句歌詞設為選中狀態就可以了。

接下來是LyricMediaInfoProvider的最簡單實現,提供了固定的歌名和歌曲檔案的位置資訊。如果需要切換歌曲,需要再複雜一些。

  1. privateclassLyricMediaInfoProviderimplementsMediaPlayerService.MediaInfoProvider{
  2. StringmUrl;
  3. StringmTitle;
  4. LyricMediaInfoProvider(Stringurl,Stringtitle){
  5. mUrl=url;
  6. mTitle=title;
  7. }
  8. @Override
  9. publicbooleanmoveToPrev(){
  10. //TODOAuto-generatedmethodstub
  11. returnfalse;
  12. }
  13. @Override
  14. publicbooleanmoveToNext(){
  15. //TODOAuto-generatedmethodstub
  16. returnfalse;
  17. }
  18. @Override
  19. publicStringgetUrl(){
  20. returnmUrl;
  21. }
  22. @Override
  23. publicStringgetTitle(){
  24. //TODOAuto-generatedmethodstub
  25. returnmTitle;
  26. }
  27. }

接下來是onCreate方法。主要做了幾件事

1.建立和LyricPlayerServiceProxy之間的聯絡。

2.提供了的實現NotificationProvider(詳細資訊請參照:Android歌詞秀設計思路(4)通用的音樂播放服務(下)

3.設定ImageButton的尺寸。

  1. /**Calledwhentheactivityisfirstcreated.*/
  2. @Override
  3. publicvoidonCreate(BundlesavedInstanceState){
  4. super.onCreate(savedInstanceState);
  5. setContentView(R.layout.main);
  6. mLyricEdit=(EditText)this.findViewById(R.id.editLyric);
  7. mProxy.setConnectionListener(this);
  8. mProxy.setLyricPlayerListener(this);
  9. mProxy.setNotificationProvider(newMediaPlayerService.NotificationProvider(){
  10. @Override
  11. publicNotificationcreateNotification(Contextcontext){
  12. Notificationnotification=newNotification(R.drawable.button_blue_play,mProxy.getTitle(),System.currentTimeMillis());
  13. //ThePendingIntenttolaunchouractivityiftheuserselectsthisnotification
  14. PendingIntentcontentIntent=PendingIntent.getActivity(context,0,newIntent(context,LyricMain.class),0);
  15. //Settheinfofortheviewsthatshowinthenotificationpanel.
  16. notification.setLatestEventInfo(context,getText(R.string.media_player_label),mProxy.getTitle(),contentIntent);
  17. returnnotification;
  18. }
  19. });
  20. mProxy.startAndBindService();
  21. mLyricEndList.clear();
  22. DisplayMetricsmetrics=newDisplayMetrics();
  23. getWindowManager().getDefaultDisplay().getMetrics(metrics);
  24. intbtnId[]={R.id.buttonPrev,R.id.buttonStop,R.id.buttonPlay,R.id.buttonPause,R.id.buttonNext};
  25. intbtnSize=Math.min(metrics.widthPixels,metrics.heightPixels)/(btnId.length+1);
  26. //調整按鍵尺寸。
  27. for(inti=0;i<btnId.length;++i){
  28. ImageButtonib=(ImageButton)this.findViewById(btnId[i]);
  29. ib.setAdjustViewBounds(true);
  30. ib.setMaxHeight(btnSize);
  31. ib.setMaxWidth(btnSize);
  32. }
  33. ImageButtonselectFile=(ImageButton)this.findViewById(R.id.buttonSelectFile);
  34. selectFile.setAdjustViewBounds(true);
  35. selectFile.setMaxHeight(btnSize*2/3);
  36. selectFile.setMaxWidth(btnSize*2/3);
  37. updateButtonState();
  38. }

再下來是onDestroy方法,如果音樂在播放中,就接觸和播放服務之間的關係,退出程式,這是歌曲播放會繼續。如果播放出於停止或暫停狀態,就連同播放服務一起關閉,完全退出程式。

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

啟動選擇檔案的SelectFileActivity

  1. publicvoidOnSelectFile(Viewv){
  2. Intenti=newIntent(this,SelectFileActivity.class);
  3. startActivityForResult(i,0);
  4. }

SelectFileActivity關閉,取得選中的媒體檔案的資訊並通知的LyricPlayerServiceProxy

接下來是按鍵處理

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

根據播放狀態更新各個按鍵的狀態。

  1. protectedvoidupdateButtonState(){
  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. //implementofLyricPlayerServiceProxy.ServiceConnectionListener
  2. publicvoidonServiceConnected(){
  3. Stringtitle=mProxy.getTitle();
  4. if(title!=null){
  5. TextViewtv=(TextView)this.findViewById(R.id.fileTitle);
  6. tv.setText(title);
  7. }
  8. updateButtonState();
  9. }
  10. publicvoidonServiceDisconnected(){
  11. }

實現LyricPlayerListener的程式碼,負責處理歌詞播放服務的各種通知。

  1. //implementofLyricPlayerService.LyricPlayerListener
  2. publicvoidonLyricLoaded(){
  3. mLyricEndList.clear();
  4. Stringlyric=newString();
  5. for(inti=0;i<mProxy.getLyricCount();++i){
  6. lyric+=mProxy.getLyric(i);
  7. lyric+="\r\n";
  8. mLyricEndList.add(newInteger(lyric.length()));
  9. }
  10. mLyricEdit.setText(lyric);
  11. }
  12. publicvoidonStateChanged(){
  13. updateButtonState();
  14. }
  15. publicvoidonPositionChanged(longposition){
  16. }
  17. publicvoidonLyricChanged(intlyric_index){
  18. intlyricStart=0;
  19. if(lyric_index>0){
  20. lyricStart=mLyricEndList.get(lyric_index-1);
  21. }
  22. intlyricEnd=mLyricEndList.get(lyric_index);
  23. mLyricEdit.setSelection(lyricStart,lyricEnd);
  24. mLyricEdit.invalidate();
  25. Log.i(TAG,String.format("lyric=%d,setSelection(%d,%d)",lyric_index,lyricStart,lyricEnd));
  26. }

在歌詞讀入時,將所有歌詞練成一個長字串,並記住每一句歌詞在字串中的位置。

在播放服務的狀態發生變化時,更新按鈕的狀態。

在當前歌詞發生變化時,根據前面儲存的位置資訊將當前歌詞設定成高亮。

最後是跳到選定歌詞的程式碼,還是一樣的簡單。

  1. publicvoidOnLyricClick(Viewv){
  2. EditTextet=(EditText)v;
  3. intsel_start=et.getSelectionStart();
  4. for(inti=0;i<mLyricEndList.size();++i){
  5. if(sel_start<mLyricEndList.get(i))
  6. {
  7. mProxy.seekToLyric(i);
  8. break;
  9. }
  10. }
  11. }

結合選中的位置,和儲存的歌詞位置資訊,找到歌詞的序號,讓播放服務跳到那句就行了。

轉載於:https://blog.51cto.com/craftsman1970/667904