Skip to content

Commit 1ecc8df

Browse files
fix(*): 优化AudioPlayerView不好用的问题
1 parent 7096da7 commit 1ecc8df

File tree

5 files changed

+93
-67
lines changed

5 files changed

+93
-67
lines changed

lite/src/main/java/com/pengxh/kt/lite/extensions/Context.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,10 +229,15 @@ fun Context.createLogFile(): File {
229229
return logFile
230230
}
231231

232+
/**
233+
* 音频编解码:AMR_WB
234+
*
235+
* 音频文件格式:.amr
236+
* */
232237
fun Context.createAudioFile(): File {
233238
val audioDir = File(this.getExternalFilesDir(Environment.DIRECTORY_MUSIC), "")
234239
val timeStamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.CHINA).format(Date())
235-
val audioFile = File(audioDir.toString() + File.separator + "AUD_" + timeStamp + ".m4a")
240+
val audioFile = File(audioDir.toString() + File.separator + "AUD_" + timeStamp + ".amr")
236241
if (!audioFile.exists()) {
237242
try {
238243
audioFile.createNewFile()

lite/src/main/java/com/pengxh/kt/lite/extensions/Int.kt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,17 @@ import androidx.core.content.ContextCompat
77
import java.math.RoundingMode
88
import java.text.DecimalFormat
99

10+
/**
11+
* 小于10首位补〇
12+
* */
13+
fun Int.appendZero(): String {
14+
return if (this < 10) {
15+
"0$this"
16+
} else {
17+
this.toString()
18+
}
19+
}
20+
1021
/**
1122
* 获取xml颜色值
1223
*/

lite/src/main/java/com/pengxh/kt/lite/widget/audio/AudioPlayerView.kt

Lines changed: 71 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,117 +1,126 @@
11
package com.pengxh.kt.lite.widget.audio
22

33
import android.content.Context
4-
import android.media.MediaPlayer
4+
import android.media.AsyncPlayer
5+
import android.media.AudioAttributes
6+
import android.media.MediaMetadataRetriever
7+
import android.net.Uri
8+
import android.os.CountDownTimer
59
import android.os.Handler
610
import android.os.Message
711
import android.util.AttributeSet
812
import android.view.View
913
import androidx.annotation.DrawableRes
1014
import androidx.appcompat.widget.AppCompatTextView
1115
import androidx.core.content.res.ResourcesCompat
16+
import com.pengxh.kt.lite.R
17+
import com.pengxh.kt.lite.extensions.appendZero
1218
import com.pengxh.kt.lite.utils.Constant
1319
import com.pengxh.kt.lite.utils.WeakReferenceHandler
1420
import java.io.File
1521
import java.io.FileInputStream
16-
import java.io.IOException
1722

18-
/**
19-
* TODO 暂时先放一放,计划重写
20-
* */
23+
2124
class AudioPlayerView(context: Context, attrs: AttributeSet) : AppCompatTextView(context, attrs),
2225
Handler.Callback, View.OnClickListener {
2326

2427
private val kTag = "AudioPlayerView"
2528
private val weakReferenceHandler by lazy { WeakReferenceHandler(this) }
26-
private lateinit var audioSource: File
27-
private var mediaPlayer: MediaPlayer? = null
28-
private var hasPrepared = false
29+
private val asyncPlayer by lazy { AsyncPlayer(kTag) }
30+
private val audioAttributes = AudioAttributes.Builder()
31+
.setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
32+
.setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
33+
.build()
2934
private var index = 0
30-
private var animationRunnable: Runnable
35+
private var isPlaying = false
36+
private var duration = 0L
37+
private lateinit var file: File
3138

3239
init {
3340
setOnClickListener(this)
34-
35-
animationRunnable = object : Runnable {
36-
override fun run() {
37-
weakReferenceHandler.postDelayed(this, 200)
38-
setDrawable(Constant.AUDIO_DRAWABLES[index % 3])
39-
index++
40-
}
41-
}
4241
}
4342

4443
fun setAudioSource(file: File) {
45-
audioSource = file
46-
initMediaPlayer()
47-
}
48-
49-
private fun initMediaPlayer() {
50-
mediaPlayer = MediaPlayer()
51-
//此时MediaPlayer进入Idle状态,不能进行任何操作,setDataSource会让播放器从Idle进入Loaded状态
52-
mediaPlayer?.apply {
53-
val inputStream = FileInputStream(audioSource)
54-
setDataSource(inputStream.fd)
55-
try {
56-
prepare()
57-
} catch (e: IOException) {
58-
e.printStackTrace()
44+
this.file = file
45+
//获取音频时长
46+
val mmr = MediaMetadataRetriever()
47+
try {
48+
val inputStream = FileInputStream(file)
49+
val fileDescriptor = inputStream.fd
50+
mmr.setDataSource(fileDescriptor)
51+
val time = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION)
52+
duration = if (time.isNullOrBlank()) {
53+
0L
54+
} else {
55+
time.toLong()
5956
}
6057

61-
setOnPreparedListener {
62-
hasPrepared = true
63-
64-
text = if (duration == -1) {
65-
""
66-
} else {
67-
val sec = duration / 1000
68-
val m = sec / 60
69-
val s = sec % 60
70-
"$m:$s"
71-
}
72-
}
58+
//格式化时长
59+
val sec = duration / 1000
60+
val m = (sec / 60).toInt().appendZero()
61+
val s = (sec % 60).toInt().appendZero()
62+
text = "$m:$s"
63+
} catch (e: Exception) {
64+
e.printStackTrace()
65+
} finally {
66+
mmr.release()
7367
}
7468
}
7569

7670
override fun onClick(v: View?) {
77-
mediaPlayer?.apply {
78-
if (isPlaying) {
79-
stop()
80-
release()
81-
stopAnimation()
82-
mediaPlayer = null
83-
} else {
84-
initMediaPlayer()
85-
start()
86-
startAnimation()
87-
}
71+
if (isPlaying) {
72+
asyncPlayer.stop()
73+
stopAnimation()
74+
isPlaying = false
75+
} else {
76+
asyncPlayer.play(context, Uri.fromFile(file), false, audioAttributes)
77+
startAnimation()
78+
isPlaying = true
79+
/**
80+
* 倒计时,判断音频是否播放完毕
81+
*
82+
* AsyncPlayer是MediaPlayer的简单异步封装,简单到只提供播放和停止API,所以需要自己监听播放是否完毕
83+
* */
84+
object : CountDownTimer(duration, 1000) {
85+
override fun onTick(millisUntilFinished: Long) {
86+
87+
}
88+
89+
override fun onFinish() {
90+
stop()
91+
}
92+
}.start()
8893
}
8994
}
9095

9196
private fun startAnimation() {
97+
//防止被多次点击,每次点击View之后都把之前的动画Runnable清空
9298
weakReferenceHandler.removeCallbacks(animationRunnable)
9399
weakReferenceHandler.postDelayed(animationRunnable, 200)
94100
}
95101

96102
private fun stopAnimation() {
97-
setDrawable(Constant.AUDIO_DRAWABLES[2])
103+
setDrawable(R.drawable.ic_audio_icon3)
98104
weakReferenceHandler.removeCallbacks(animationRunnable)
99105
}
100106

101-
//暂时只能设置在左边,后期改为可设置方向
107+
private var animationRunnable = object : Runnable {
108+
override fun run() {
109+
weakReferenceHandler.postDelayed(this, 200)
110+
setDrawable(Constant.AUDIO_DRAWABLES[index % 3])
111+
index++
112+
}
113+
}
114+
102115
private fun setDrawable(@DrawableRes id: Int) {
103116
val drawable = ResourcesCompat.getDrawable(context.resources, id, null)!!
104-
drawable.setBounds(0, 0, drawable.minimumWidth, drawable.minimumHeight)
117+
drawable.setBounds(0, 0, drawable.intrinsicWidth, drawable.intrinsicHeight)
105118
setCompoundDrawables(drawable, null, null, null)
106119
}
107120

108121
fun stop() {
109-
mediaPlayer?.apply {
110-
stop()
111-
release()
112-
}
113122
weakReferenceHandler.removeCallbacks(animationRunnable)
114-
mediaPlayer = null
123+
isPlaying = false
115124
}
116125

117126
override fun handleMessage(msg: Message): Boolean {

lite/src/main/java/com/pengxh/kt/lite/widget/audio/AudioRecorder.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,16 +38,16 @@ class AudioRecorder constructor(private val context: Context) : Handler.Callback
3838
}
3939
mediaRecorder?.apply {
4040
setAudioSource(MediaRecorder.AudioSource.MIC) // 设置麦克风
41-
setOutputFormat(MediaRecorder.OutputFormat.DEFAULT)
42-
setAudioEncoder(MediaRecorder.AudioEncoder.AAC)
41+
setOutputFormat(MediaRecorder.OutputFormat.AMR_WB)
42+
setAudioEncoder(MediaRecorder.AudioEncoder.AMR_WB)
4343
setOutputFile(audioFile.absolutePath)
4444
setMaxDuration(Constant.MAX_LENGTH)
4545
prepare()
4646
}
4747
}
4848

4949
/**
50-
* 开始录音 使用m4a格式
50+
* 开始录音 使用amr格式
5151
*
5252
* @return
5353
*/

lite/src/main/res/values/style.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,9 @@
5252

5353
<style name="AudioPlayerViewBaseStyle">
5454
<item name="android:layout_width">wrap_content</item>
55-
<item name="android:layout_height">@dimen/lib_dp_40</item>
55+
<item name="android:layout_height">@dimen/lib_dp_35</item>
5656
<item name="android:paddingStart">@dimen/lib_dp_10</item>
57+
<item name="android:paddingEnd">@dimen/lib_dp_10</item>
5758
<item name="android:drawablePadding">@dimen/lib_dp_10</item>
5859
<item name="android:drawableStart">@drawable/ic_audio_icon3</item>
5960
<item name="android:gravity">left|center_vertical</item>

0 commit comments

Comments
 (0)