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

如何在onCreate()中正确访问ViewModel的UI状态以实现UI自动更新

问题解决方案

一、UI不更新的核心原因与修复

你在Activity中把uiState赋值给普通的lateinit var,这只是获取了ViewModel中uiState的初始快照,后续ViewModel更新状态时,Activity里的变量不会同步,Composable也无法感知变化。正确的做法是让Composable直接观察ViewModel的可观察状态,触发自动重组。

关键修改点:

  1. 在Activity中用Kotlin委托正确获取ViewModel:
    private val viewModel: CreateGameViewModel by viewModels()
    
  2. 在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 04:55:07