安卓拓展 MediaPlayer 实现淡入淡出效果
@Moriafly 未经允许,严禁搬运抄袭
在椒盐音乐开发中有需求,播放暂停需要实现淡入淡出效果,就是在暂停时慢慢减弱音量而在播放时缓慢增加音量。
MediaPlayer 是 android.meida 下的类,拓展性比不上 ExoPlayer,但是对于这样的淡入淡出效果还是很好实现的。
为什么选择拓展 MediaPlayer 而不是在业务代码中拓展嗯?其实还是因为播放器的播放暂停(按照唯一来源设计)应该只有一处实现调用 MediaPlayer 的 start() 和 pause() ,那就是在媒体会话的回调中(MediaSession),例子:
mediaSessionCallback = object : MediaSessionCompat.Callback() {
override fun onPlay() {
mediaPlayer.start()
}
override fun onPause() {
mediaPlayer.pause()
}
}
而在 onPlay() 和 onPause() 中往往不仅仅是调用播放器的播放暂停,还有如音频焦点管理、耳机拔出噪音广播注册等等。而音频淡入淡出效果设计差值器、播放器状态管理(比如点击暂停,不应该是直接暂停而是当声音慢慢到 0 才暂停音乐,这个过程也是出于 isPlaying 暂停)等等。
话不多说,既然要拓展,那就写个之类继承自 MediaPlayer 。
class AudioPlayer(): MediaPlayer() {
}
setVolume 逻辑改变
我们知道 setVolume(left, right) 是更改左右声道音量大小,而我们的淡入淡出也需要通过这个,但是可能其他时候我们可能也需要调整音量大小,这个多为音效中的“左右声道平衡”。那么这样如果用户需求左右声道音量不一致的话,我们的淡入淡出需要考虑的事情就十分的多:
需要通过百分比计算减少的音量;
用户先前设置的左右声道平衡的数值的记录和保存;
等等。
所以我们可以将其分开,让淡入淡出完全占用 setVolume 的使用权。而左右声道平衡另外考虑。
左右声道平衡的另外考虑
/** 左声道 */
var leftChannel: Float = 1F
/** 右声道 */
var rightChannel: Float = 1F
@OverridePrivate
override fun setVolume(leftVolume: Float, rightVolume: Float) {
super.setVolume(
leftVolume * leftChannel,
rightVolume * rightChannel
)
}
淡入淡出的差值变化(以暂停举例)
private var volume = 1F
private var isPauseSmoothing: Boolean = false
private val pauseSmoothValueAnimator = ValueAnimator.ofFloat(1F, 0F).apply {
duration = 500L
interpolator = LinearInterpolator()
addUpdateListener {
volume = it.animatedValue as Float
try {
// 此时可能 MediaPlayer 状态发生了改变,所以用 try catch 包裹,一旦发生错误,立马取消
setVolume(volume, volume)
} catch (e: Exception) {
it.cancel()
}
}
addListener(object : Animator.AnimatorListener {
override fun onAnimationStart(animation: Animator?) { }
override fun onAnimationEnd(animation: Animator?) {
this@AudioPlayer.setVolume(0F, 0F)
this@AudioPlayer.pause()
isPauseSmoothing = false
}
override fun onAnimationCancel(animation: Animator?) {
isPauseSmoothing = false
}
override fun onAnimationRepeat(animation: Animator?) { }
})
}
那么我们可以写个缓慢暂停函数
fun pauseSmooth() {
isPauseSmoothing = true
pauseSmoothValueAnimator.start()
}
注意记得重写 isPlaying 和 reset 函数
override fun isPlaying(): Boolean {
if (isPauseSmoothing) {
return false
}
return super.isPlaying()
}
override fun reset() {
startSmoothValueAnimator.cancel()
super.reset()
}
播放也是同理。
这样差不多就完成了,实测效果很棒,还可以设置淡入淡出时长,通过设置 ValueAnimator 的 duration 数值。
完整代码
已经发布在 Github 。
链接:https://github.com/Moriafly/SaltPlayerSource/blob/main/source_code/player/AudioPlayer.kt