我們在 /res/raw 文件夾下放一個 audio_bird.mp3 並通過 MediaPlayer 來播放。
這裡是 MediaPlayer 的一些常用方法
var mediaPlayer = MediaPlayer.create(this, R.raw.audio_bird) - 創建 MediaPlayer
mediaPlayer.start() - 開始播放檔案(會從最後停止的地方開始)
mediaPlayer.pause() - 暫停播放檔案
mediaPlayer.reset() - 重置(目前測試重置後沒辦法馬上重新播放)
mediaPlayer.isPlaying - 返回當前的播放狀態
mediaPlayer.seekTo() - 移動檔案播放進度(單位毫秒)
mediaPlayer.currentPosition - 取得當前的播放進度(單位毫秒)
mediaPlayer.duration - 取得檔案總時間(單位毫秒)
mediaPlayer.setVolume() - 設置左右聲道的音量
監聽 MediaPlayer 是否播放完畢的方法
mediaPlayer.setOnCompletionListener{}
volumeSeekBar.setOnSeekBarChangeListener(object:SeekBar.OnSeekBarChangeListener{
override fun onProgressChanged(p0: SeekBar?, progress: Int, p2: Boolean) {
// update progress text
volumeTextView.setText("Volumn: ${volumeSeekBar.progress}%")
// update volumn
mediaPlayer.setVolume(progress / 100f, progress / 100f)
}
override fun onStartTrackingTouch(p0: SeekBar?) {}
override fun onStopTrackingTouch(p0: SeekBar?) {}
})
通過 Thread 每個 500 毫秒根據播放進度來更新當前 SeekBar 的進度
val thread = Thread(Runnable {
while (true) {
Thread.sleep(500)
if (!isSeeking) {
progressSeekBar.progress = mediaPlayer.currentPosition
}
}
})
thread.start()
首先給 progressSeekBar 設定最大值為播放檔案的總時間
// tootal time duration of sonng
progressSeekBar.max = mediaPlayer.duration
建立一個 isSeeking 來判斷使用者是否拖動進度條
private var isSeeking = false
給 progressSeekBar 加入監聽,當使用者拖動進度條的時候 isSeeking 改為 true 並且同步 MediaPlayer 播放的進度
progressSeekBar.setOnSeekBarChangeListener(object:SeekBar.OnSeekBarChangeListener{
override fun onProgressChanged(p0: SeekBar?, progress: Int, p2: Boolean) {
if(isSeeking) {
mediaPlayer.seekTo(progressSeekBar.progress)
}
}
override fun onStartTrackingTouch(p0: SeekBar?) {
isSeeking = true
}
override fun onStopTrackingTouch(p0: SeekBar?) {
isSeeking = false
}
})
在使用者開始播放音樂的時候,通過 ObjectAniamtor 來旋轉小鳥的圖片
lateinit var birdAnimator:ObjectAnimator
private fun startAnimateBirdImageView() {
if(birdAnimator.isPaused){
birdAnimator.resume()
} else {
birdAnimator.start()
}
}
private fun pauseAnimateBirdImageView() {
birdAnimator.pause()
}
private fun resetAnimateBirdImageView() {
birdAnimator.end()
}
在播放的過程中,會不斷地去檢查 mediaPlayer.currentPostion 進而更新進度條
// continuously updating progress
val thread = Thread(Runnable {
while (true) {
Thread.sleep(500)
if (!isSeeking) {
progressSeekBar.progress = mediaPlayer.currentPosition
}
}
})
thread.start()
當音樂播放完畢之後,將小鳥圖片、進度條、播放按鈕還原(播放按鈕呈現 PLAY 字樣)
播放音樂的過程中,小鳥的圖片會不停的旋轉
離開 Activity 的時候,音樂、小鳥動畫停止。
需要用到錄音和文件寫入的權限(在 AndroidManifest.xml 中加入)
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
可以通過 PackageManager 來檢查設備有沒有麥克風
val packageManager = packageManager
if(!packageManager.hasSystemFeature(PackageManager.FEATURE_MICROPHONE)){
Log.e("PackageManager","This device doesn't have a mic")
}
通過 ActivityCompat 來檢查錄音的權限。
if(ActivityCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.RECORD_AUDIO), 0)
return
}
通過 ActivityCompat 檢查寫入文件的權限。
if(ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),0)
return
}
檢查完寫入權限以後,通過 File.CreateTempFile 建立一個臨時的錄音檔案為「birdRecording.3gp」
val path = File(Environment.getExternalStorageDirectory().path)
try {
soundFile = File.createTempFile ("birdRecording", ".3gp", path)
println("created file $soundFile")
} catch (e: IOException) {
Log.e("Setup sound File","failed ${e.message}")
}
臨時的錄音檔會被加入亂數後綴
初始化 MediaRecorder 並且設定使用麥克風、輸出格式、編碼格式。
記得錄音前要先執行 prepare 方法。
recorder = MediaRecorder()
recorder.setAudioSource(MediaRecorder.AudioSource.MIC)
recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP)
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB)
recorder.setOutputFile(soundFile?.absolutePath)
try {
recorder.prepare()
} catch (e: IOException) {
}
recorder.start()
通過 stop 方法結束錄音,並通過 release 方法釋放資源
recorder.stop()
recorder.release()
初始化 MediaPlayer 並準備好兩個 Listener 一個等 player 準備好以後播放,一個等播放完成時重置 player
player = MediaPlayer()
player!!.setOnPreparedListener(playerPreparedHandler)
player!!.setOnCompletionListener {
stopPlayer()
}
接下來我們要讓 player 直到我們要播放什麼檔案。
前面錄音的時候,我們通過 var soundFile 紀錄臨時建立的檔案,這裡通過 absolutePath 方法來拿到路徑。
try {
player?.setDataSource(soundFile!!.absolutePath)
player?.prepare()
} catch(e: IOException) {
Log.e("PlayRecording","Failed")
}
在 player 準備好以後,會執行準備好的 playerPreparedHandler
player?.start()
而 player 播放完畢的時候,會執行我們的 stopPlayer()
player?.release()
player = null