編寫:kesenhoo - 原文:http://developer.android.com/training/managing-audio/volume-playback.html
良好的用戶體驗(yàn)應(yīng)該是可預(yù)期且可控的。如果我們的應(yīng)用可以播放音頻,那么顯然我們需要做到能夠通過硬件按鈕,軟件按鈕,藍(lán)牙耳麥等來控制音量。 同樣地,我們需要能夠?qū)?yīng)用的音頻流進(jìn)行播放(Play),停止(Stop),暫停(Pause),跳過(Skip),以及回放(Previous)等動(dòng)作,并且并確保其正確性。
為了創(chuàng)建一個(gè)良好的音頻體驗(yàn),我們首先需要知道應(yīng)用會(huì)使用到哪些音頻流。Android為播放音樂,鬧鈴,通知鈴,來電聲音,系統(tǒng)聲音,打電話聲音與撥號(hào)聲音分別維護(hù)了一個(gè)獨(dú)立的音頻流。這樣做的主要目的是讓用戶能夠單獨(dú)地控制不同的種類的音頻。上述音頻種類中,大多數(shù)都是被系統(tǒng)限制。例如,除非你的應(yīng)用需要做替換鬧鐘的鈴聲的操作,不然的話你只能通過STREAM_MUSIC來播放你的音頻。
默認(rèn)情況下,按下音量控制鍵會(huì)調(diào)節(jié)當(dāng)前被激活的音頻流,如果我們的應(yīng)用當(dāng)前沒有播放任何聲音,那么按下音量鍵會(huì)調(diào)節(jié)響鈴的音量。對(duì)于游戲或者音樂播放器而言,即使是在歌曲之間無聲音的狀態(tài),或是當(dāng)前游戲處于無聲的狀態(tài),用戶按下音量鍵的操作通常都意味著他們希望調(diào)節(jié)游戲或者音樂的音量。你可能希望通過監(jiān)聽音量鍵被按下的事件,來調(diào)節(jié)音頻流的音量。其實(shí)我們不必這樣做。Android提供了setVolumeControlStream()方法來直接控制指定的音頻流。在鑒別出應(yīng)用會(huì)使用哪個(gè)音頻流之后,我們需要在應(yīng)用生命周期的早期階段調(diào)用該方法,因?yàn)樵摲椒ㄖ恍枰贏ctivity整個(gè)生命周期中調(diào)用一次,通常,我們可以在負(fù)責(zé)控制多媒體的Activity或者Fragment的onCreate()
方法中調(diào)用它。這樣能確保不管應(yīng)用當(dāng)前是否可見,音頻控制的功能都能符合用戶的預(yù)期。
setVolumeControlStream(AudioManager.STREAM_MUSIC);
自此之后,不管目標(biāo)Activity或Fragment是否可見,按下設(shè)備的音量鍵都能夠影響我們指定的音頻流(在這個(gè)例子中,音頻流是"music")。
許多線控或者無線耳機(jī)都會(huì)有許多媒體播放控制按鈕,例如:播放,停止,暫停,跳過,以及回放等。無論用戶按下設(shè)備上任意一個(gè)控制按鈕,系統(tǒng)都會(huì)廣播一個(gè)帶有ACTION_MEDIA_BUTTON的Intent。為了正確地響應(yīng)這些操作,需要在Manifest文件中注冊(cè)一個(gè)針對(duì)于該Action的BroadcastReceiver,如下所示:
<receiver android:name=".RemoteControlReceiver">
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON" />
</intent-filter>
</receiver>
在Receiver的實(shí)現(xiàn)中,需要判斷這個(gè)廣播來自于哪一個(gè)按鈕,Intent通過EXTRA_KEY_EVENT這一Key包含了該信息,另外,KeyEvent類包含了一系列諸如KEYCODE_MEDIA_*
的靜態(tài)變量來表示不同的媒體按鈕,例如KEYCODE_MEDIA_PLAY_PAUSE 與 KEYCODE_MEDIA_NEXT。
public class RemoteControlReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction())) {
KeyEvent event = (KeyEvent)intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
if (KeyEvent.KEYCODE_MEDIA_PLAY == event.getKeyCode()) {
// Handle key press.
}
}
}
}
因?yàn)榭赡軙?huì)有多個(gè)程序在監(jiān)聽與媒體按鈕相關(guān)的事件,所以我們必須在代碼中控制應(yīng)用接收相關(guān)事件的時(shí)機(jī)。下面的例子顯示了如何使用AudioManager來為我們的應(yīng)用注冊(cè)監(jiān)聽與取消監(jiān)聽媒體按鈕事件,當(dāng)Receiver被注冊(cè)上時(shí),它將是唯一一個(gè)能夠響應(yīng)媒體按鈕廣播的Receiver。
AudioManager am = mContext.getSystemService(Context.AUDIO_SERVICE);
...
// Start listening for button presses
am.registerMediaButtonEventReceiver(RemoteControlReceiver);
...
// Stop listening for button presses
am.unregisterMediaButtonEventReceiver(RemoteControlReceiver);
通常,應(yīng)用需要在他們失去焦點(diǎn)或者不可見的時(shí)候(比如在onStop()方法里面)取消注冊(cè)監(jiān)聽。但是對(duì)于媒體播放應(yīng)用來說并沒有那么簡單,實(shí)際上,在應(yīng)用不可見(不能通過可見的UI控件進(jìn)行控制)的時(shí)候,仍然能夠響應(yīng)媒體播放按鈕事件是極其重要的。為了實(shí)現(xiàn)這一點(diǎn),有一個(gè)更好的方法,我們可以在程序獲取與失去音頻焦點(diǎn)的時(shí)候注冊(cè)與取消對(duì)音頻按鈕事件的監(jiān)聽。這個(gè)內(nèi)容會(huì)在后面的課程中詳細(xì)講解。