如何在UI层响应ViewModel中CountDownTimer的onTick事件并播放声音?
解决方案:在UI层响应ViewModel的倒计时回调播放音频
由于ViewModel不应持有Context或处理UI相关操作(比如音频播放),推荐以下几种可行方案:
方案一:观察ViewModel中的remainingTime状态
ViewModel已经在onTick中更新了_remainingTime,UI层可以直接观察这个状态变量,每次值变化时触发音频播放:
ViewModel代码(无需修改原有逻辑)
fun startTimer() { stopAllTimers() _isRunning.value = true countDownTimer = object : CountDownTimer(duration, INTERVAL) { override fun onTick(millisUntilFinished: Long) { _remainingTime.value = millisUntilFinished / 1000 } override fun onFinish() {} }.start() }
UI层(Activity/Fragment示例)
在UI层监听remainingTime的变化,触发音频播放:
// 以Activity为例,在onCreate中设置观察 viewModel.remainingTime.observe(this) { playTickSound() } // 音频播放实现示例 private fun playTickSound() { val mediaPlayer = MediaPlayer.create(this, R.raw.tick_sound) mediaPlayer.start() mediaPlayer.setOnCompletionListener { it.release() } }
方案二:通过回调接口传递onTick事件
定义回调接口,ViewModel持有接口的弱引用(避免内存泄漏),UI层实现接口并传入ViewModel,在onTick中触发回调:
定义回调接口
interface TimerTickListener { fun onTimerTick() }
ViewModel修改
// 使用WeakReference防止内存泄漏 private var tickListener: WeakReference<TimerTickListener>? = null fun setTimerTickListener(listener: TimerTickListener?) { tickListener = listener?.let { WeakReference(it) } } fun startTimer() { stopAllTimers() _isRunning.value = true countDownTimer = object : CountDownTimer(duration, INTERVAL) { override fun onTick(millisUntilFinished: Long) { _remainingTime.value = millisUntilFinished / 1000 // 调用UI层的回调方法 tickListener?.get()?.onTimerTick() } override fun onFinish() {} }.start() }
UI层实现接口
class YourActivity : AppCompatActivity(), TimerTickListener { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) viewModel.setTimerTickListener(this) } override fun onTimerTick() { playTickSound() } private fun playTickSound() { // 音频播放逻辑 } override fun onDestroy() { super.onDestroy() // 清空引用,避免泄漏 viewModel.setTimerTickListener(null) } }
方案三:使用SharedFlow发送事件
如果使用Kotlin协程,可以在ViewModel中用SharedFlow发送tick事件,UI层收集事件并处理:
ViewModel代码
private val _tickEvents = MutableSharedFlow<Unit>() val tickEvents = _tickEvents.asSharedFlow() fun startTimer() { stopAllTimers() _isRunning.value = true countDownTimer = object : CountDownTimer(duration, INTERVAL) { override fun onTick(millisUntilFinished: Long) { _remainingTime.value = millisUntilFinished / 1000 // 发送tick事件 viewModelScope.launch { _tickEvents.emit(Unit) } } override fun onFinish() {} }.start() }
UI层收集事件
lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.tickEvents.collect { playTickSound() } } }
内容的提问来源于stack exchange,提问作者jaskaran singh
相关产品推荐
相关产品推荐

