Android Studio 學習(三)
技術標籤:android
Android Studio 學習(三)
服務與廣播
學習目標:
1、掌握服務的基本概念,能編寫服務過程並進行呼叫;
2、掌握廣播的基本概念,能實現廣播通訊。
3、需實現的具體功能為:
簡訊到來時自動產生的系統廣播→啟用音樂播放服務程式→活動元件程式使得停止按鈕可用。
專案功能說明:
1、音樂播放介面能顯示歌曲封面、歌曲名、作者、播放進度、當前時間、歌曲總時間
2、能實現上一首、下一首、暫停、播放、停止,並且封面隨之改變
3、拖動進度條可以對應的改變歌曲的播放進度
4、頁面佈局清晰,執行流暢
核心程式碼:
音樂播放頁面由基本資訊介面、控制介面兩個介面組成,分別對應兩個LinearLayout
基本資訊介面中的第一個LinearLayout中有兩個TextView分別對應歌曲名、作者
基本資訊介面中的第二個LinearLayout中包含兩個TextView和一個SeeBar分別對應當前時間、歌曲總時間、進度條
控制介面包含四個ImageButton分別對應上一首、播放(暫停)、停止、下一首
基本資訊介面:歌曲名、作者
<LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal"> <TextView android:id="@+id/title" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_weight="1" android:background="#ffffff" android:text="Title" android:textSize="20dp" /> <TextView android:id="@+id/author" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:background="#ffffff" android:text="Author" android:textSize="20dp" /> </LinearLayout>
基本資訊介面:當前時間、進度條、歌曲總時間
<LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal"> <TextView android:id="@+id/curTime" android:layout_width="wrap_content" android:layout_height="match_parent" android:background="#ffffff" android:text="00:00" /> <SeekBar android:id="@+id/seekBar" android:layout_width="310dp" android:layout_height="wrap_content" android:background="#ffffff" android:scrollbarSize="20sp" /> <TextView android:id="@+id/endTime" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#ffffff" android:text="00:00" /> </LinearLayout>
控制介面
<LinearLayout
android:layout_width="match_parent"
android:layout_height="45dp"
android:orientation="horizontal">
<ImageButton
android:id="@+id/ptrack"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#ffffff"
app:srcCompat="@drawable/ptract" />
<ImageButton
android:id="@+id/play"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#ffffff"
app:srcCompat="@drawable/play" />
<ImageButton
android:id="@+id/stop"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#ffffff"
app:srcCompat="@drawable/stop" />
<ImageButton
android:id="@+id/ntrack"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#ffffff"
app:srcCompat="@drawable/ntract" />
</LinearLayout>
事件控制的設計
控制流程:
①點選按鈕 --> ②設定intent,廣播intent並將被MusicService中的MyReceiver 接收到 --> ③根據廣播的intent進行音樂的播放、暫停、切換以及對應進度條的控制 --> ④設定intent,廣播intent並將被MusicboxFragement中的MyReceiver接收到 --> ⑤MusicboxFragement更改播放(暫停)圖示、歌曲封面、歌曲名、作者名
若在歌曲播放的過程中,點選按鈕則執行上述迴圈,否則歌曲播放完成後自動播放下一首並執行上述迴圈(無①點選按鈕)
重要程式碼:
點選事件
public void onClick(View view) {
// 建立 Intent
Intent intent = new Intent("org.mywechat.action.CTL_ACTION");
switch (view.getId()) {
//按下播放/暫停按鈕
case R.id.play:
intent.putExtra("control", 1);
break;
// 按下 停止 按鈕
case R.id.stop:
intent.putExtra("control", 2);
break;
// 按下 上一首
case R.id.ptrack:
intent.putExtra("control", 3);
break;
// 按下 下一首
case R.id.ntrack:
intent.putExtra("control", 4);
break;
}
// 傳送廣播,將被Service 元件 中的BroadcastReceiver 接收到
getActivity().sendBroadcast(intent);
}
MusicFragement中的MyReceiver
public class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// 獲取 Intent 中的update訊息,update 代表播放狀態
int update = intent.getIntExtra("update", -1);
// 獲取 Intent 中的current訊息,current代表當前正在播放的歌曲
int current = intent.getIntExtra("current", -1);
if (current >= 0) {
title.setText(titleStrs[current]);
author.setText(authorStrs[current]);
imag.setImageResource(imagStrs[current]);
}
switch (update) {
//沒有 播放狀態
case 0x11:
play.setImageResource(R.drawable.play);
status = 0x11;
title.setText("");
author.setText("");
imag.setImageResource(R.drawable.music);
break;
//控制系統進入 播放狀態
case 0x12:
//播放狀態下設定使用暫停圖示
play.setImageResource(R.drawable.pause);
//設定當前狀態
status = 0x12;
break;
//控制系統進入暫停狀態
case 0x13:
//暫停狀態下設定使用播放圖示
play.setImageResource(R.drawable.play);
//設定當前狀態
status = 0x13;
break;
}
}
}
MusicService中的MyReceiver
public void onReceive(Context context, Intent intent) {
int control = intent.getIntExtra("control", -1);
switch (control) {
//播放或暫停
case 1:
//原來處於沒有播放狀態
if (status == 0x11) {
// 準備 並播放 音樂
prepareAndPlay(musics[current]);
status = 0x12;
}
//原來處於播放狀態
else if (status == 0x12) {
//暫停
mediaPlayer.pause();
//改為暫停狀態
status = 0x13;
}
// 原來 處於 暫停狀態
else if (status == 0x13) {
//播放
mediaPlayer.start();
// 改變狀態
status = 0x12;
}
break;
//停止 播放
case 2:
//如果原來正在播放或暫停
if (status == 0x12 || status == 0x13) {
// 停止播放
mediaPlayer.pause();
mediaPlayer.seekTo(0);
status = 0x11;
}
break;
// 上一首
case 3:
if (status == 0x12 || status == 0x13) {
current = ((current - 1) + musics.length) % musics.length;
prepareAndPlay(musics[current]);
status = 0x12;
}
break;
// 下一首
case 4:
if (status == 0x12 || status == 0x13) {
current = (current + 1) % musics.length;
prepareAndPlay(musics[current]);
status = 0x12;
}
break;
}
// 廣播通知 MusicboxFragment 更改圖示、文字框
Intent sendIntent = new Intent(MusicboxFragment.UPDATE_ACTION);
sendIntent.putExtra("update", status);
sendIntent.putExtra("current", current);
//傳送廣播,將被MusicboxFragment元件中的BroadcastReceiver接收到
sendBroadcast(sendIntent);
}
}
音樂準備和播放
private void prepareAndPlay(String music) {
int max;
// 開啟指定音樂檔案
try {
AssetFileDescriptor afd = assetManager.openFd(music);
mediaPlayer.reset();
// 使用MediaPlayer載入指定的聲音檔案。
mediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
// 準備聲音
mediaPlayer.prepare();
max = mediaPlayer.getDuration();
seekBar.setMax(max);
endTime.setText(MusicService.formatTime(max));
//建立一個程序來控制進度條
handler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
seekBar.setProgress(mediaPlayer.getCurrentPosition());
}
};//訊息傳遞
DelayThread delaythread = new DelayThread(100);
delaythread.start();
//播放
mediaPlayer.start();
} catch (IOException e) {
e.printStackTrace();
}
}
MediaPlayer完成事件監聽
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
//實現自動播放
current = (current + 1) % musics.length;
//傳送廣播 通知 MusicboxFragment 更改文字框
Intent sendIntent = new Intent(MusicboxFragment.UPDATE_ACTION);
sendIntent.putExtra("current", current);
// 傳送廣播將被MusicboxFragment元件中的BroadcastReceiver 接收到
sendBroadcast(sendIntent);
//準備並播放音樂
prepareAndPlay(musics[current]);
}
});
seekBar監聽
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
//設定當前時間
curTime.setText(MusicService.formatTime(i));
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
//拖動跳轉
mediaPlayer.seekTo(seekBar.getProgress());
}
});
控制seekBar的程序
class DelayThread extends Thread {
int milliseconds;
public DelayThread(int i) {
milliseconds = i;
}
public void run() {
while (true) {
try {
sleep(milliseconds);
//設定音樂進度讀取頻率
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
handler.sendEmptyMessage(0);
}
}
}
執行截圖:
小結
本專案的難點在於Service和Fragement之間相互廣播以及進度條的設定。Service和Fragement之間相互廣播的關鍵是要弄清楚整個專案的事件控制流程(參考事件“控制流程的設計”)。進度條的設定需要使用Handler,以及多程序,使用Handler讓SeekBar獲取歌曲的實時位置,然後在開一個程序handler.sendEmptyMessage(0).
專案原始碼:https://gitee.com/chenranranran/mobile-terminal-development.git