如何在onCreate()中正确访问ViewModel的UI状态以实现UI自动更新
问题解决方案
一、UI不更新的核心原因与修复
你在Activity中把uiState赋值给普通的lateinit var,这只是获取了ViewModel中uiState的初始快照,后续ViewModel更新状态时,Activity里的变量不会同步,Composable也无法感知变化。正确的做法是让Composable直接观察ViewModel的可观察状态,触发自动重组。
关键修改点:
- 在Activity中用Kotlin委托正确获取ViewModel:
private val viewModel: CreateGameViewModel by viewModels() - 在Composable内部通过
collectAsState()(或直接引用mutableStateOf对象)观察uiState,确保状态变化能触发UI重组:val uiState = viewModel.uiState.collectAsState()
二、viewModel()函数的说明
官方文档中的viewModel()是Composable函数,只能在Composable代码块内使用。如果在Activity(非Composable)中获取ViewModel,使用by viewModels()委托是Kotlin的标准写法,对应你看到的viewModels<? extends ViewModel>()的Kotlin版本。
三、Flow使用的错误修正
你的Flow用法存在逻辑问题:Dao返回的Flow<List<GameType>>是持续的数据流,先调用result.first()会仅获取第一个值,后续的collect虽然能监听变化,但会导致重复赋值。正确的做法是直接收集整个Flow,在数据流回调中更新UI状态。
修改后的完整代码
ViewModel部分
data class CreateGameUIState( val types: List<GameType> = emptyList(), val fetching: Boolean = false ) class CreateGameViewModel : ViewModel() { // 私有setter确保状态仅能在ViewModel内部修改 var uiState by mutableStateOf(CreateGameUIState()) private set init { viewModelScope.launch { uiState = uiState.copy(fetching = true) // 直接收集Flow,监听数据库数据变化 MyApp.getInstance().database.gameTypeDao().getGameTypes() .collect { gameTypes -> uiState = uiState.copy( fetching = false, types = gameTypes ) } } } }
Activity部分
class CreateGameActivity : ComponentActivity() { private val viewModel: CreateGameViewModel by viewModels() @OptIn(ExperimentalMaterial3Api::class) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { MyAppTheme { val uiState = viewModel.uiState.collectAsState() Scaffold( topBar = { TopAppBar(title = { Text(stringResource(R.string.act_creategame_name)) }) } ) { innerPadding -> Column(modifier = Modifier.padding(innerPadding)) { Row { if (uiState.value.fetching) { Text("Loading data...") } else { for ((index, gameType) in uiState.value.types.withIndex()) { val id = resources.getIdentifier(gameType.icon_name, "drawable", packageName) if (id != 0) { Image( modifier = Modifier .width(10.dp) .height(10.dp) .clickable { Toast.makeText(baseContext, "", Toast.LENGTH_LONG).show() }, painter = painterResource(id = id), contentDescription = null ) } } } } } } } } } }
内容的提问来源于stack exchange,提问作者Jan Thürmann
相关产品推荐
相关产品推荐

