我們用瞭6篇文章的篇幅做瞭鋪墊,終於到瞭真正的應用程序瞭。這部分還是一如既往的簡單。
有關應用的類有兩個,一個是LiryicMain,一個是SelectFileActivity。都是差不多最低限度的內容,沒有任何華麗的內容。
先看看這兩個類在整個軟件中的位置。從圖中可以看出LyricMain是軟件全體的控制者。SelectFileActivity也為LyricMain提供服務。
SelectFileActivity太過簡單,本文中就不再說明瞭。我們集中篇幅說明一下LyricMain。
首先是數據成員。一個是LyricPlayerServiceProxy,歌詞播放服務的代理,一個是用來保存歌詞結束位置的List。
private LyricPlayerServiceProxy mProxy = new LyricPlayerServiceProxy(this);
private ArrayList<Integer> mLyricEndList = new ArrayList<Integer>();
LyricPlayerServiceProxy是前面已經介紹過的內容,在這裡就不在重復瞭。mLyricEndList需要說明一下。在這個軟件中我們將所有歌詞都表示在一個TextEditView中,為瞭能夠表示當前播放中的歌詞,我們將每一句歌詞的位置保存在mLyricEndList中,這樣當播放中的歌詞發生變化時,隻要將這句歌詞設為選中狀態就可以瞭。
接下來是LyricMediaInfoProvider的最簡單實現,提供瞭固定的歌名和歌曲文件的位置信息。如果需要切換歌曲,需要再復雜一些。
private class LyricMediaInfoProvider implements MediaPlayerService.MediaInfoProvider{
String mUrl;
String mTitle;
LyricMediaInfoProvider(String url, String title){
mUrl = url;
mTitle = title;
}
@Override
public boolean moveToPrev() {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean moveToNext() {
// TODO Auto-generated method stub
return false;
}
@Override
public String getUrl() {
return mUrl;
}
@Override
public String getTitle() {
// TODO Auto-generated method stub
return mTitle;
}
}
接下來是onCreate方法。主要做瞭幾件事
1.建立和LyricPlayerServiceProxy之間的聯系。
2.提供瞭的實現NotificationProvider(詳細信息請參照: Android歌詞秀設計思路(4)通用的音樂播放服務(下) )
3.設置ImageButton的尺寸。
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mLyricEdit = (EditText)this.findViewById(R.id.editLyric);
mProxy.setConnectionListener(this);
mProxy.setLyricPlayerListener(this);
mProxy.setNotificationProvider(new MediaPlayerService.NotificationProvider(){
@Override
public Notification createNotification(Context context) {
Notification notification = new Notification(R.drawable.button_blue_play, mProxy.getTitle(), System.currentTimeMillis());
// The PendingIntent to launch our activity if the user selects this notification
PendingIntent contentIntent = PendingIntent.getActivity(context, 0, new Intent(context, LyricMain.class), 0);
// Set the info for the views that show in the notification panel.
notification.setLatestEventInfo(context, getText(R.string.media_player_label), mProxy.getTitle(), contentIntent);
return notification;
}
});
mProxy.startAndBindService();
mLyricEndList.clear();
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
int btnId[] = {R.id.buttonPrev, R.id.buttonStop, R.id.buttonPlay, R.id.buttonPause, R.id.buttonNext};
int btnSize = Math.min(metrics.widthPixels, metrics.heightPixels) / (btnId.length + 1);
//調整按鍵尺寸。
for(int i = 0; i < btnId.length; ++i){
ImageButton ib = (ImageButton)this.findViewById(btnId[i]);
ib.setAdjustViewBounds(true);
ib.setMaxHeight(btnSize);
ib.setMaxWidth(btnSize);
}
ImageButton selectFile = (ImageButton)this.findViewById(R.id.buttonSelectFile);
selectFile.setAdjustViewBounds(true);
selectFile.setMaxHeight(btnSize*2/3);
selectFile.setMaxWidth(btnSize*2/3);
updateButtonState();
}
再下來是onDestroy方法,如果音樂在播放中,就接觸和播放服務之間的關系,退出程序,這是歌曲播放會繼續。如果播放出於停止或暫停狀態,就連同播放服務一起關閉,完全退出程序。
@Override
protected void onDestroy() {
super.onDestroy();
mProxy.setConnectionListener(null);
mProxy.setLyricPlayerListener(null);
if(!mProxy.isPlaying()){
mProxy.stopService();
}
}
啟動選擇文件的SelectFileActivity
public void OnSelectFile(View v){
Intent i = new Intent(this, SelectFileActivity.class);
startActivityForResult(i, 0);
}
SelectFileActivity關閉,取得選中的媒體文件的信息並通知的LyricPlayerServiceProxy
接下來是按鍵處理
public void OnOperationButtonClick(View v){
switch(v.getId()){
case R.id.buttonPrev:
mProxy.seekToPrevLyric();
break;
case R.id.buttonStop:
if(mProxy.isPlaying() || mProxy.isPausing()){
mProxy.stop();
}
break;
case R.id.buttonPlay:
if(!mProxy.isPlaying()){
mProxy.start();
}
break;
case R.id.buttonPause:
if(mProxy.isPlaying()){
mProxy.pause();
}
break;
case R.id.buttonNext:
mProxy.seekToNextLyric();
break;
}
}
根據播放狀態更新各個按鍵的狀態。
protected void updateButtonState(){
((ImageButton)this.findViewById(R.id.buttonPrev)).setEnabled(mProxy.isPlaying() || mProxy.isPausing());
((ImageButton)this.findViewById(R.id.buttonStop)).setEnabled(mProxy.isPlaying() || mProxy.isPausing());
((ImageButton)this.findViewById(R.id.buttonPlay)).setEnabled(mProxy.getDataSource()!= null && (!mProxy.isPlaying() || mProxy.isPausing()));
((ImageButton)this.findViewById(R.id.buttonPause)).setEnabled(mProxy.isPlaying());
((ImageButton)this.findViewById(R.id.buttonNext)).setEnabled(mProxy.isPlaying() || mProxy.isPausing());
}
如果是程序啟動時已經有歌曲在播放,就更新一下文件標題和按鈕狀態。
//implement of LyricPlayerServiceProxy.ServiceConnectionListener
public void onServiceConnected(){
String title = mProxy.getTitle();
if(title != null){
TextView tv = (TextView)this.findViewById(R.id.fileTitle);
tv.setText(title);
}
updateButtonState();
}
public void onServiceDisconnected(){
}
實現LyricPlayerListener的代碼,負責處理歌詞播放服務的各種通知。
//implement of LyricPlayerService.LyricPlayerListener
public void onLyricLoaded(){
mLyricEndList.clear();
String lyric = new String();
for(int i = 0; i < mProxy.getLyricCount(); ++i){
lyric += mProxy.getLyric(i);
lyric += "\r\n";
mLyricEndList.add(new Integer(lyric.length()));
}
mLyricEdit.setText(lyric);
}
public void onStateChanged(){
updateButtonState();
}
public void onPositionChanged(long position){
}
public void onLyricChanged(int lyric_index){
int lyricStart = 0;
if(lyric_index > 0){
lyricStart = mLyricEndList.get(lyric_index – 1);
}
int lyricEnd = mLyricEndList.get(lyric_index);
mLyricEdit.setSelection(lyricStart, lyricEnd);
mLyricEdit.invalidate();
Log.i(TAG, String.format("lyric= %d, setSelection(%d, %d)", lyric_index, lyricStart, lyricEnd));
}
在歌詞讀入時,將所有歌詞練成一個長字符串,並記住每一句歌詞在字符串中的位置。
在播放服務的狀態發生變化時,更新按鈕的狀態。
在當前歌詞發生變化時,根據前面保存的位置信息將當前歌詞設置成高亮。
最後是跳到選定歌詞的代碼,還是一樣的簡單。
public void OnLyricClick(View v){
EditText et = (EditText)v;
int sel_start = et.getSelectionStart();
for(int i = 0; i < mLyricEndList.size(); ++i){
if(sel_start < mLyricEndList.get(i))
{
mProxy.seekToLyric(i);
break;
}
}
}
結合選中的位置,和保存的歌詞位置信息,找到歌詞的序號,讓播放服務跳到那句就行瞭。
作者 “來自大連”