編輯:關於Android編程
如果我們的應用能夠播放音頻,那麼讓用戶能夠以自己預期的方式控制音頻是很重要的。為了保證良好的用戶體驗,我們應該讓應用能夠管理當前的音頻焦點,因為這樣才能確保多個應用不會在同一時刻一起播放音頻。
在學習本系列課程中,我們將會創建可以對音量按鈕進行響應的應用,該應用會在播放音頻的時候請求獲取音頻焦點,並且在當前音頻焦點被系統或其他應用所改變的時候,做出正確的響應。
控制音量與音頻播放(Controlling Your App’s Volume and Playback)
學習如何確保用戶能通過硬件或軟件音量控制器調節應用的音量(通常這些控制器上還具有播放、停止、暫停、跳過以及回放等功能按鍵)。
管理音頻焦點(Managing Audio Focus)
由於可能會有多個應用具有播放音頻的功能,考慮他們如何交互非常重要。為了防止多個音樂應用同時播放音頻,Android使用音頻焦點(Audio Focus)來控制音頻的播放。在這節課中可以學習如何請求音頻焦點,監聽音頻焦點的丟失,以及在這種情況發生時應該如何做出響應。
兼容音頻輸出設備(Dealing with Audio Output Hardware)
音頻有多種輸出設備,在這節課中可以學習如何找出播放音頻的設備,以及處理播放時耳機被拔出的情況。
編寫:kesenhoo- 原文:http://developer.android.com/training/managing-audio/volume-playback.html
良好的用戶體驗應該是可預期且可控的。如果我們的應用可以播放音頻,那麼顯然我們需要做到能夠通過硬件按鈕,軟件按鈕,藍牙耳麥等來控制音量。 同樣地,我們需要能夠對應用的音頻流進行播放(Play),停止(Stop),暫停(Pause),跳過(Skip),以及回放(Previous)等動作,並且並確保其正確性。
為了創建一個良好的音頻體驗,我們首先需要知道應用會使用到哪些音頻流。Android為播放音樂,鬧鈴,通知鈴,來電聲音,系統聲音,打電話聲音與撥號聲音分別維護了一個獨立的音頻流。這樣做的主要目的是讓用戶能夠單獨地控制不同的種類的音頻。上述音頻種類中,大多數都是被系統限制。例如,除非你的應用需要做替換鬧鐘的鈴聲的操作,不然的話你只能通過STREAM_MUSIC來播放你的音頻。
默認情況下,按下音量控制鍵會調節當前被激活的音頻流,如果我們的應用當前沒有播放任何聲音,那麼按下音量鍵會調節響鈴的音量。對於游戲或者音樂播放器而言,即使是在歌曲之間無聲音的狀態,或是當前游戲處於無聲的狀態,用戶按下音量鍵的操作通常都意味著他們希望調節游戲或者音樂的音量。你可能希望通過監聽音量鍵被按下的事件,來調節音頻流的音量。其實我們不必這樣做。Android提供了setVolumeControlStream()方法來直接控制指定的音頻流。在鑒別出應用會使用哪個音頻流之後,我們需要在應用生命周期的早期階段調用該方法,因為該方法只需要在Activity整個生命周期中調用一次,通常,我們可以在負責控制多媒體的Activity或者Fragment的onCreate()
方法中調用它。這樣能確保不管應用當前是否可見,音頻控制的功能都能符合用戶的預期。
setVolumeControlStream(AudioManager.STREAM_MUSIC);
自此之後,不管目標Activity或Fragment是否可見,按下設備的音量鍵都能夠影響我們指定的音頻流(在這個例子中,音頻流是"music")。
許多線控或者無線耳機都會有許多媒體播放控制按鈕,例如:播放,停止,暫停,跳過,以及回放等。無論用戶按下設備上任意一個控制按鈕,系統都會廣播一個帶有ACTION_MEDIA_BUTTON的Intent。為了正確地響應這些操作,需要在Manifest文件中注冊一個針對於該Action的BroadcastReceiver,如下所示:
在Receiver的實現中,需要判斷這個廣播來自於哪一個按鈕,Intent通過EXTRA_KEY_EVENT這一Key包含了該信息,另外,KeyEvent類包含了一系列諸如KEYCODE_MEDIA_*
的靜態變量來表示不同的媒體按鈕,例如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.
}
}
}
}
因為可能會有多個程序在監聽與媒體按鈕相關的事件,所以我們必須在代碼中控制應用接收相關事件的時機。下面的例子顯示了如何使用AudioManager來為我們的應用注冊監聽與取消監聽媒體按鈕事件,當Receiver被注冊上時,它將是唯一一個能夠響應媒體按鈕廣播的Receiver。
AudioManager am = mContext.getSystemService(Context.AUDIO_SERVICE);
...
// Start listening for button presses
am.registerMediaButtonEventReceiver(RemoteControlReceiver);
...
// Stop listening for button presses
am.unregisterMediaButtonEventReceiver(RemoteControlReceiver);
通常,應用需要在他們失去焦點或者不可見的時候(比如在onStop()方法裡面)取消注冊監聽。但是對於媒體播放應用來說並沒有那麼簡單,實際上,在應用不可見(不能通過可見的UI控件進行控制)的時候,仍然能夠響應媒體播放按鈕事件是極其重要的。為了實現這一點,有一個更好的方法,我們可以在程序獲取與失去音頻焦點的時候注冊與取消對音頻按鈕事件的監聽。這個內容會在後面的課程中詳細講解。
編寫:kesenhoo- 原文:http://developer.android.com/training/managing-audio/audio-focus.html
由於可能會有多個應用可以播放音頻,所以我們應當考慮一下他們應該如何交互。為了防止多個音樂播放應用同時播放音頻,Android使用音頻焦點(Audio Focus)來控制音頻的播放——即只有獲取到音頻焦點的應用才能夠播放音頻。
在我們的應用開始播放音頻之前,它需要先請求音頻焦點,然後再獲取到音頻焦點。另外,它還需要知道如何監聽失去音頻焦點的事件並對此做出合適的響應。
在我們的應用開始播放音頻之前,它需要獲取將要使用的音頻流的音頻焦點。通過使用requestAudioFocus()方法可以獲取我們希望得到的音頻流焦點。如果請求成功,該方法會返回AUDIOFOCUS_REQUEST_GRANTED。
另外我們必須指定正在使用的音頻流,而且需要確定所請求的音頻焦點是短暫的(Transient)還是永久的(Permanent)。
短暫的焦點鎖定:當計劃播放一個短暫的音頻時使用(比如播放導航指示)。 永久的焦點鎖定:當計劃播放一個較長但時長可預期的音頻時使用(比如播放音樂)。下面的代碼片段是一個在播放音樂時請求永久音頻焦點的例子,我們必須在開始播放之前立即請求音頻焦點,比如在用戶點擊播放或者游戲中下一關的背景音樂開始前。
AudioManager am = mContext.getSystemService(Context.AUDIO_SERVICE);
...
// Request audio focus for playback
int result = am.requestAudioFocus(afChangeListener,
// Use the music stream.
AudioManager.STREAM_MUSIC,
// Request permanent focus.
AudioManager.AUDIOFOCUS_GAIN);
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
am.registerMediaButtonEventReceiver(RemoteControlReceiver);
// Start playback.
}
一旦結束了播放,需要確保調用了abandonAudioFocus()方法。這樣相當於告知系統我們不再需要獲取焦點並且注銷所關聯的AudioManager.OnAudioFocusChangeListener監聽器。對於另一種釋放短暫音頻焦點的情況,這會允許任何被我們打斷的應用可以繼續播放。
// Abandon audio focus when playback complete
am.abandonAudioFocus(afChangeListener);
當請求短暫音頻焦點的時候,我們可以選擇是否開啟“Ducking”。通常情況下,一個應用在失去音頻焦點時會立即關閉它的播放聲音。如果我們選擇在請求短暫音頻焦點的時候開啟了Ducking,那意味著其它應用可以繼續播放,僅僅是在這一刻降低自己的音量,直到重新獲取到音頻焦點後恢復正常音量(譯注:也就是說,不用理會這個短暫焦點的請求,這並不會打斷目前正在播放的音頻。比如在播放音樂的時候突然出現一個短暫的短信提示聲音,此時僅僅是把歌曲的音量暫時調低,使得用戶能夠聽到短信提示聲,在此之後便立馬恢復正常播放)。
// Request audio focus for playback
int result = am.requestAudioFocus(afChangeListener,
// Use the music stream.
AudioManager.STREAM_MUSIC,
// Request permanent focus.
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
// Start playback.
}
Ducking對於那些間歇性使用音頻焦點的應用來說特別合適,比如語音導航。
如果有另一個應用像上述那樣請求音頻焦點,它所請求的永久音頻焦點或者短暫音頻焦點(支持Ducking或不支持Ducking),都會被你在請求獲取音頻焦點時所注冊的監聽器接收到。
如果應用A請求獲取了音頻焦點,那麼在應用B請求獲取音頻焦點的時候,A獲取到的焦點就會失去。如何響應失去焦點事件,取決於失去焦點的方式。
在音頻焦點的監聽器裡面,當接受到描述焦點改變的事件時會觸發onAudioFocusChange()回調方法。如之前提到的,獲取焦點有三種類型,我們同樣會有三種失去焦點的類型:永久失去,短暫失去,允許Ducking的短暫失去。
失去短暫焦點:通常在失去短暫焦點的情況下,我們會暫停當前音頻的播放或者降低音量,同時需要准備在重新獲取到焦點之後恢復播放。
失去永久焦點:假設另外一個應用開始播放音樂,那麼我們的應用就應該有效地將自己停止。在實際場景當中,這意味著停止播放,移除媒體按鈕監聽,允許新的音頻播放器可以唯一地監聽那些按鈕事件,並且放棄自己的音頻焦點。此時,如果想要恢復自己的音頻播放,我們需要等待某種特定用戶行為發生(例如按下了我們應用當中的播放按鈕)。
在下面的代碼片段當中,如果焦點的失去是短暫型的,我們將音頻播放對象暫停,並在重新獲取到焦點後進行恢復。如果是永久型的焦點失去事件,那麼我們的媒體按鈕監聽器會被注銷,並且不再監聽音頻焦點的改變。
OnAudioFocusChangeListener afChangeListener = new OnAudioFocusChangeListener() {
public void onAudioFocusChange(int focusChange) {
if (focusChange == AUDIOFOCUS_LOSS_TRANSIENT
// Pause playback
} else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
// Resume playback
} else if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
am.unregisterMediaButtonEventReceiver(RemoteControlReceiver);
am.abandonAudioFocus(afChangeListener);
// Stop playback
}
}
};
在上面失去短暫焦點的例子中,如果允許Ducking,那麼除了暫停當前的播放之外,我們還可以選擇使用“Ducking”。
在使用Ducking時,正常播放的歌曲會降低音量來凸顯這個短暫的音頻聲音,這樣既讓這個短暫的聲音比較突出,又不至於打斷正常的聲音。
下面的代碼片段讓我們的播放器在暫時失去音頻焦點時降低音量,並在重新獲得音頻焦點之後恢復原來音量。
OnAudioFocusChangeListener afChangeListener = new OnAudioFocusChangeListener() {
public void onAudioFocusChange(int focusChange) {
if (focusChange == AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
// Lower the volume
} else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
// Raise it back to normal
}
}
};
音頻焦點的失去是我們需要響應的最重要的事件廣播之一,但除此之外還有很多其他重要的廣播需要我們正確地做出響應。系統會廣播一系列的Intent來向你告知用戶在使用音頻過程當中的各種變化。下節課會演示如何監聽這些廣播並提升用戶的整體體驗。
編寫:kesenhoo- 原文:http://developer.android.com/training/managing-audio/audio-output.html
當用戶想要通過Android設備欣賞音樂的時候,他可以有多種選擇,大多數設備擁有內置的揚聲器,有線耳機,也有其它很多設備支持藍牙連接,有些甚至還支持A2DP藍牙音頻傳輸模型協定。(譯注:A2DP全名是Advanced Audio Distribution Profile 藍牙音頻傳輸模型協定! A2DP是能夠采用耳機內的芯片來堆棧數據,達到聲音的高清晰度。有A2DP的耳機就是藍牙立體聲耳機。聲音能達到44.1kHz,一般的耳機只能達到8kHz。如果手機支持藍牙,只要裝載A2DP協議,就能使用A2DP耳機了。還有消費者看到技術參數提到藍牙V1.0 V1.1 V1.2 V2.0 - 這些是指藍牙的技術版本,是指通過藍牙傳輸的速度,他們是否支持A2DP具體要看藍牙產品制造商是否使用這個技術。來自百度百科)
使用不同的硬件播放聲音會影響到應用的行為。可以使用AudioManager來查詢當前音頻是輸出到揚聲器,有線耳機還是藍牙上,如下所示:
if (isBluetoothA2dpOn()) {
// Adjust output for Bluetooth.
} else if (isSpeakerphoneOn()) {
// Adjust output for Speakerphone.
} else if (isWiredHeadsetOn()) {
// Adjust output for headsets
} else {
// If audio plays and noone can hear it, is it still playing?
}
當有線耳機被拔出或者藍牙設備斷開連接的時候,音頻流會自動輸出到內置的揚聲器上。假設播放聲音很大,這個時候突然轉到揚聲器播放會顯得非常嘈雜。
幸運的是,系統會在這種情況下廣播帶有ACTION_AUDIO_BECOMING_NOISY的Intent。無論何時播放音頻,我們都應該注冊一個BroadcastReceiver來監聽這個Intent。在使用音樂播放器時,用戶通常會希望此時能夠暫停當前歌曲的播放。而在游戲當中,用戶通常會希望可以減低音量。
private class NoisyAudioStreamReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(intent.getAction())) {
// Pause the playback
}
}
}
private IntentFilter intentFilter = new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
private void startPlayback() {
registerReceiver(myNoisyAudioStreamReceiver(), intentFilter);
}
private void stopPlayback() {
unregisterReceiver(myNoisyAudioStreamReceiver);
}
Android插件化基礎(4),動態啟動插件中的ActivityAuthor:鄭海波-莫川簡介如何動態啟動插件中的Activity呢?我們首先分析,啟動插件中的Activ
1.概述 最近一直都在帶實習生做項目,發現自己好久沒有寫博客了,這幾天更新會比較頻繁,今天玩QQ的時候發現QQ主頁菜單滑動效果早就變了,實在忍不住晚上就來實現一下了!
在上一篇雷達圖中留下了一個坑——折線圖。折線圖(broken-line graph)大概是初中數學就開始學習的,用來統計一段時間內某個數據的趨勢。
無限自動循環 = 無限循環 + 自動循環無限循環 = 無限向左循環 + 無限向右循環接下來我們通過demo一步步的實現無限向右循環–>無限向左循環&nd