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
相关产品推荐
相关产品推荐

