我正在try 理解如何使用保存可以传递给不同屏幕的值的ViewModel.

我已经编写了一个非常简单的程序来try 理解这个概念.

以下是我的主要活动:

sealed class Destination(val route: String) {
    object PlayerSelection: Destination("PlayerSelection")
    object TwoPlayerGame: Destination("TwoPlayerGame")
    object ThreePlayerGame: Destination("ThreePlayerGame")
    object FourPlayerGame: Destination("FourPlayerGame")
}


class MainActivity : ComponentActivity() {

    private val viewModel: MainViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            Viewmodels_and_NavigationTheme {
                // A surface container using the 'background' color from the theme
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {



                    ScreenSetup(viewModel)



                }
            }
        }
    }
}

以下是我的NavigationApphost:


@Composable
fun NavigationAppHost (navController: NavController, viewModel: MainViewModel) {

    NavHost(navController = navController as NavHostController, startDestination = "PlayerSelection") {
        composable(Destination.PlayerSelection.route)  { PlayerSelection(navController,MainViewModel()) }
        composable(Destination.TwoPlayerGame.route) {TwoPlayerGame(navController,MainViewModel())}
        composable(Destination.ThreePlayerGame.route) {ThreePlayerGame(navController,MainViewModel())}
        composable(Destination.FourPlayerGame.route) {FourPlayerGame(navController,MainViewModel())}
    }


}

下面是我的ScreenSetup:


@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ScreenSetup(viewModel: MainViewModel) {

//Navigation Stuff
    val navController = rememberNavController()

    Scaffold(

        topBar = { TopAppBar(title = {  Text(text = "Viewmodels and Navigation") }) },
        content = { padding ->

            Column(Modifier.padding(padding)) {

                NavigationAppHost(navController = navController,viewModel)


            }
        },
        bottomBar = { Text(text = "BottomBar") }


    )



}

以下是Composable PlayerSelection:


@Composable
fun PlayerSelection(navController: NavController, viewModel: MainViewModel) {
    val HowManyPlayers: Int by viewModel.howManyPlayers.collectAsState()


    Column {
        Row {
            Text(text = "How Many Players?")
        }
        Row() {

            Button(onClick = { viewModel.Selection(2) }) {
                Text("Two Players")
            }
            Button(onClick = { viewModel.Selection(3) }) {
                Text("Three Players")
            }
            Button(onClick = { viewModel.Selection(4) }) {
                Text("Four Players")
            }
            
              }
        Row() {
            Text(text = "Current Value Held in MainViewModel = ${HowManyPlayers}")
        }

        Button(onClick = {

            if (HowManyPlayers == 2) {
                navController.navigate(Destination.TwoPlayerGame.route)
                      }
            if (HowManyPlayers == 3) {
                navController.navigate(Destination.ThreePlayerGame.route)
            }
            if (HowManyPlayers == 4) {
                navController.navigate(Destination.FourPlayerGame.route)
            }

        }) {
            Text(text = "Click To Navigate To Next Screen")
        }


    }

}

以下是MainViewModel:


class MainViewModel : ViewModel() {
    var _howManyPlayers = MutableStateFlow(0)
    var howManyPlayers = _howManyPlayers.asStateFlow()



    fun Selection(players:Int) {
    _howManyPlayers.value = players
    
    }
}

下面是TwoPlayerGame Composable:

@Composable
fun TwoPlayerGame(navController: NavController, viewModel: MainViewModel) {

    val howManyPlayers by viewModel.howManyPlayers.collectAsState()


    Column {

        Row(Modifier.padding(50.dp)) {
            Text(text = "Two Player Game")
        }
        Row {
            Text(text = "Value of howManyPlayers in viewmodel = ${howManyPlayers}")
        }

    }

}

当用户在PlayerSelection屏幕中 Select 有多少玩家时,MainViewModel内的HowManyPlayers变量会因用户按下哪个按钮而成功更改.我可以看到这条线很管用:

Text(text = "Current Value Held in MainViewModel = ${HowManyPlayers}")

这真是太酷了.但是,当我单击按钮导航到TwoPlayerGame时,MainViewModel中的变量HowManyPlayers再次重置为0.为什么在导航到新屏幕时,MainViewModel中的HowManyPlayers值重置为0?有没有一种方法可以将一个ViewModel传递给不同的屏幕,同时在传递的ViewModel中保留变量值?

谢谢你考虑我的问题,我真的很感激!

推荐答案

共享数据有两种方式.

首先,您可以使用导航参数来传递参数.传递一些值并在屏幕上获得该值. https://developer.android.com/jetpack/compose/navigation#nav-with-args

其次,使用存储库层来共享一些数据.公开存储库中的流,并在ViewModel中收集此值.然后,通过存储库中的流发出一些数据. https://developer.android.com/topic/architecture/data-layer

也许,您可以通过将NavigationAppHost函数修改为传递viewModel来与一个ViewModel共享数据,而不是创建如下所示的新实例.

@Composable
fun NavigationAppHost (navController: NavController, viewModel: MainViewModel) {

    NavHost(navController = navController as NavHostController, startDestination = "PlayerSelection") {
        composable(Destination.PlayerSelection.route)  { PlayerSelection(navController, viewModel) }
        composable(Destination.TwoPlayerGame.route) {TwoPlayerGame(navController, viewModel)}
        composable(Destination.ThreePlayerGame.route) {ThreePlayerGame(navController, viewModel)}
        composable(Destination.FourPlayerGame.route) {FourPlayerGame(navController, viewModel)}
    }
}

AAC(Android架构组件)建议每个屏幕使用一个ViewModel.因为它保存了特定的UI状态,当发生配置更改时可以销毁这些状态.但是,MVVM模式还有另一种观点,比如在许多屏幕上重用ViewModel. 你可以参考AAC ViewModelMVVM ViewModel

Here are good examples to learn ViewModel and Navigation with Jetpack Compose. https://github.com/android/socialite
https://github.com/android/nowinandroid [ADVANCED]

Android相关问答推荐

如何将文本相对于喷气背包中的图标垂直居中?

懒惰列的滚动到项目不按预期工作'

显示本地房间数据库中未保存的项目的动态列表

在Android上使用XSLT文件转换XML文件

使用Kotline绑定时,ViewHolder无法识别文本视图

如何在使用带有底部导航组件的片段管理器时更改片段工具栏的标签

Android v31 及更低版本中 ImageView 中的圆角

如何从包装在泛型中的 retrofit 调用中检索密钥?

Android 设备断开连接后发送的 BLE 蓝牙数据

如何在 Jetpack Compose 中向图像视图添加对角色带?

如何在 BasicTextField 中全选焦点

如何在 Dolphin 中启用 android studio new logcat | 2021.3.1 金丝雀 6?

在alert 对话框生成器中启动协程

在 Kotlin 客户端应用程序中发送 FCM 推送通知 - Firebase 云消息传递

更新后 Firebase 服务无法在模拟器上运行

Android Jetpack Compose - 找不到 R.drawable?

如何使用 Jetpack Compose 在应用程序中实现本地化

如何让用户与任意应用程序共享文件?

WearOS - 有没有办法从心电图传感器收集原始数据?

将生成的 AAR 与 Composables 一起使用时未解决的参考