You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MutableState未触发UI重组:ExoPlayer播放状态更新无响应

问题:ExoPlayer音轨管理ViewModel中播放/暂停状态无法触发UI重组

ViewModel代码

@HiltViewModel
open class BaseViewModel @Inject constructor(
    private val context: Context,
    private val myPlayer: MyPlayer
) : ViewModel(), PlayerEvents {

 private val _selectedTrack = mutableStateOf<Track?>(null)
    var selectedTrack =_selectedTrack


  private fun onTrackSelected(index: Int) {
        if (selectedTrackIndex == -1) isTrackPlay = true
        if (selectedTrackIndex == -1 || selectedTrackIndex != index) {
            _tracks.resetTracks()
            selectedTrackIndex = index
            _selectedTrack.value = tracks[selectedTrackIndex]
            setUpTrack()
        }
    }

override fun onPlayPauseClick() {
        myPlayer.playPause()
        _selectedTrack.value = _selectedTrack.value?.apply { state = myPlayer.playerState.value }
    }

}

UI代码

Image(
    modifier = Modifier.clickable {
        onPlayPause()
    },
    painter = painterResource(
        id = if (selectedTrack.state == PlayerStates.STATE_PLAYING) R.drawable.ic_pause
        else R.drawable.ic_play
    ),
    contentDescription = "pause",
    colorFilter = ColorFilter.tint(
        MaterialTheme.colorScheme.onSurface,
        BlendMode.SrcIn
    )
)

问题原因

Compose的MutableState通过引用变化触发UI重组:

  • onTrackSelected中直接赋值了新的Track实例(tracks[selectedTrackIndex]),对象引用改变,因此能触发重组。
  • onPlayPauseClick中使用apply仅修改了现有Track对象的state属性,但对象引用未变,Compose检测不到值的变化,因此不会触发UI更新。
  • 替换为MutableStateFlow无效也是同样原因:StateFlow仅在值(引用)发生变化时发射新事件,修改对象内部属性不会触发新事件。

解决方案

方案1:创建Track新实例(推荐)

确保Track是data class,利用其自动生成的copy方法创建新实例,替换原有引用:

override fun onPlayPauseClick() {
    myPlayer.playPause()
    val currentTrack = _selectedTrack.value ?: return
    // 通过copy创建新实例并修改state
    _selectedTrack.value = currentTrack.copy(state = myPlayer.playerState.value)
}

如果Track不是data class,需要手动实现复制逻辑,确保返回新的对象实例。

方案2:单独维护播放状态的State

将播放状态从Track中抽离,单独用MutableState管理:

// ViewModel中新增
private val _currentPlayState = mutableStateOf(PlayerStates.STATE_IDLE)
val currentPlayState = _currentPlayState

override fun onPlayPauseClick() {
    myPlayer.playPause()
    _currentPlayState.value = myPlayer.playerState.value
    // 可选:同步更新selectedTrack中的state以保持数据一致性
    _selectedTrack.value = _selectedTrack.value?.apply { state = _currentPlayState.value }
}

UI中直接使用独立的状态判断:

painter = painterResource(
    id = if (currentPlayState == PlayerStates.STATE_PLAYING) R.drawable.ic_pause
    else R.drawable.ic_play
)

方案3:使用可观察属性(不推荐)

让Track的state属性成为可观察类型(如MutableState),但这种方式会增加数据复杂度,不符合Compose推荐的不可变数据模式。

内容的提问来源于stack exchange,提问作者annng

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 20:47:19