从 ViewModel 更新状态流时,Ui 不会重构

问题描述 投票:0回答:1

我正在尝试通过 Codelab 学习 kotlin,最近刚刚了解了 viewModel、uiState、UDF 等的使用。 (刚刚完成了乱码代码实验室)作为代码实验室之间的间歇,我涉足了我想为自己创建的应用程序,作为应用我所学到的知识的一种方式,即使很明显我无法完成它,直到我会学到更多东西。尽管如此,一旦我学到了新的东西,我经常会尝试将其付诸实践,作为在 Codelab 之外更熟悉 kotlin 的一种方式。 上次我尝试为我的应用程序创建一个 ViewModel-UiState-UiScreen 结构,该结构之前只是单个 MainActivity 文件中的一堆可组合项。

因为我还是一个初学者,所以我尝试了自己笨拙的方法,通过使用状态字符串变量和 when 条件来调用不同的组合来制作具有可组合项的多屏幕 Ui。在引入 VievModel 之前,单击 UI 中的按钮只需为状态字符串变量分配与新“屏幕”相对应的值;现在的想法是做几乎相同的事情,通过调用 VievModel 的处理函数并最终向它们传递必要的参数,以便它们可以更新数据,更改作为 uiState 一部分的相同状态变量,最后更新uiState 因此理想地触发 UI 的重组,就像以前发生的那样。那不会发生。

通过调试和日志警报,我设法验证 UI 是否正确启动,当条件启动第一个“屏幕”时,按钮调用 VieWmodel 处理函数并且数据更新为正确的值,但重组不会触发:我可以按下按钮并看到多条日志消息,确认数据和状态流已更新,但 When 循环永远不会重新启动,并且屏幕保持不变。 我实际上没有必要为此获得一个工作代码,因为当我经历更多的代码实验室时,我必须多次更改它,但我担心到目前为止我误解了一些重要的东西,这让我很烦恼。我只是想了解我错过了整件事。

// UI SCREEN: the main composable supposed to manage my screens:

@Composable
fun BigBrother(gViewModel: GViewModel = viewModel())
{
    val gUiState by gViewModel.uiState.collectAsState()

    Log.d(TAG, "Chosen path is ${gUiState.direction}")

    when(gUiState.direction) {
        "START" -> StartScreen()
        "DISPLAY" -> Header(item=gUiState.item)
        "EDITHEADER" -> EditHeader(item=gUiState.item)
        "CHANGER" ->  Changer(parameter = gUiState.parameterChange)
        "CHOOSER" -> Chooser(modifier = Modifier)
        "GALLERY" -> ScrollItems(catalogue = gUiState.catalogItem, modifier = Modifier)
        else -> StartScreen() 
    }

}

@Composable
fun StartScreen( modifier: Modifier = Modifier) {

    Log.d(TAG, "Launching START")

    Column(modifier = Modifier.fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center) {
        Text(
            text = stringResource(R.string.appname_txt)       
        )

        Button(
            onClick = {GViewModel().redirection("CHOOSER")
                Log.d(TAG, "Button1 clicked, launching CHOOSER")},
            modifier = Modifier,
        ) {
            Text(
                text = "New",             
            )
        }
        Button(
            onClick = {GViewModel().redirection("GALLERY")
                Log.d(TAG, "Button2 clicked, launching GALLERY")},
            modifier = Modifier,
        ) {

            Text(
                text = "Browse",
                modifier = Modifier
            )
        }
    }
}


// uiState:

data class GUiState(
    val item: Item = Item(....),
    val catalogItem: List<Item> = listOf(Item(...),
    val direction:String = "START",
    val parameterChange: Parameter = Parameter("New",14)
)




//  ViewModel

class GViewModel: ViewModel() {
    private val _uiState = MutableStateFlow(GUiState())
    val uiState: StateFlow<GUiState> = _uiState.asStateFlow()

....

fun redirection(newDirection:String) {
    _uiState.update { currentState -> currentState.copy(direction=newDirection)}
    Log.d(TAG, "changed UIstate direction to ${uiState.value.direction}")
}


为了澄清结果,我可以在 logcat 中看到来自“重定向”函数的消息,从而确认 _uiState 的更新,但随后我再也没有看到来自 UiScreen 的关于路径新选择的另一条日志消息。

android-jetpack-compose viewmodel composable mutablestateflow
1个回答
0
投票

在“开始屏幕”中,您可以执行以下操作:

GViewModel().重定向(“CHOOSER”)

这将创建一个新的 GViewModel 实例(带有

GViewModel()
),您可以通过调用
redirection
来设置状态。

这不是您想要的:您想要更改已经存在的视图模型实例,因为只有该实例才会被观察到更改。要实现这一点,您需要向 StartScreen 传递应调用的函数: @Composable fun StartScreen( redirection: (String) -> Unit, modifier: Modifier = Modifier, ) { // ... Button(onClick = { redirection("CHOOSER") }) { } // ... }

然后您可以像这样传递函数:

@Composable fun BigBrother(gViewModel: GViewModel = viewModel()) { val gUiState by gViewModel.uiState.collectAsState() when (gUiState.direction) { "START" -> StartScreen(redirection = gViewModel::redirection) // ... } }

因此,每当您的 StartScreen 调用 
redirection

时,实际执行的是

redirection
的 gViewModel 对象中的
BigBrother
。由于观察到该对象的
uiState
发生变化,现在它实际上会产生效果。
    

© www.soinside.com 2019 - 2024. All rights reserved.