android 尝试使用Jetpack Compose理解ViewModels和Navigation

tez616oj  于 2024-01-04  发布在  Android
关注(0)|答案(1)|浏览(166)

我试图理解如何使用ViewModel,它保存可以传递到不同屏幕的值。
我写了一个非常简单的程序来尝试理解这个概念。
以下是我的主要活动:

  1. sealed class Destination(val route: String) {
  2. object PlayerSelection: Destination("PlayerSelection")
  3. object TwoPlayerGame: Destination("TwoPlayerGame")
  4. object ThreePlayerGame: Destination("ThreePlayerGame")
  5. object FourPlayerGame: Destination("FourPlayerGame")
  6. }
  7. class MainActivity : ComponentActivity() {
  8. private val viewModel: MainViewModel by viewModels()
  9. override fun onCreate(savedInstanceState: Bundle?) {
  10. super.onCreate(savedInstanceState)
  11. setContent {
  12. Viewmodels_and_NavigationTheme {
  13. // A surface container using the 'background' color from the theme
  14. Surface(
  15. modifier = Modifier.fillMaxSize(),
  16. color = MaterialTheme.colorScheme.background
  17. ) {
  18. ScreenSetup(viewModel)
  19. }
  20. }
  21. }
  22. }
  23. }

字符串
下面是我的NavigationAppHost:

  1. @Composable
  2. fun NavigationAppHost (navController: NavController, viewModel: MainViewModel) {
  3. NavHost(navController = navController as NavHostController, startDestination = "PlayerSelection") {
  4. composable(Destination.PlayerSelection.route) { PlayerSelection(navController,MainViewModel()) }
  5. composable(Destination.TwoPlayerGame.route) {TwoPlayerGame(navController,MainViewModel())}
  6. composable(Destination.ThreePlayerGame.route) {ThreePlayerGame(navController,MainViewModel())}
  7. composable(Destination.FourPlayerGame.route) {FourPlayerGame(navController,MainViewModel())}
  8. }
  9. }


下面是我的ScreenSetup:

  1. @OptIn(ExperimentalMaterial3Api::class)
  2. @Composable
  3. fun ScreenSetup(viewModel: MainViewModel) {
  4. //Navigation Stuff
  5. val navController = rememberNavController()
  6. Scaffold(
  7. topBar = { TopAppBar(title = { Text(text = "Viewmodels and Navigation") }) },
  8. content = { padding ->
  9. Column(Modifier.padding(padding)) {
  10. NavigationAppHost(navController = navController,viewModel)
  11. }
  12. },
  13. bottomBar = { Text(text = "BottomBar") }
  14. )
  15. }


以下是Composable PlayerSelection:

  1. @Composable
  2. fun PlayerSelection(navController: NavController, viewModel: MainViewModel) {
  3. val HowManyPlayers: Int by viewModel.howManyPlayers.collectAsState()
  4. Column {
  5. Row {
  6. Text(text = "How Many Players?")
  7. }
  8. Row() {
  9. Button(onClick = { viewModel.Selection(2) }) {
  10. Text("Two Players")
  11. }
  12. Button(onClick = { viewModel.Selection(3) }) {
  13. Text("Three Players")
  14. }
  15. Button(onClick = { viewModel.Selection(4) }) {
  16. Text("Four Players")
  17. }
  18. }
  19. Row() {
  20. Text(text = "Current Value Held in MainViewModel = ${HowManyPlayers}")
  21. }
  22. Button(onClick = {
  23. if (HowManyPlayers == 2) {
  24. navController.navigate(Destination.TwoPlayerGame.route)
  25. }
  26. if (HowManyPlayers == 3) {
  27. navController.navigate(Destination.ThreePlayerGame.route)
  28. }
  29. if (HowManyPlayers == 4) {
  30. navController.navigate(Destination.FourPlayerGame.route)
  31. }
  32. }) {
  33. Text(text = "Click To Navigate To Next Screen")
  34. }
  35. }
  36. }


下面是MainViewModel:

  1. class MainViewModel : ViewModel() {
  2. var _howManyPlayers = MutableStateFlow(0)
  3. var howManyPlayers = _howManyPlayers.asStateFlow()
  4. fun Selection(players:Int) {
  5. _howManyPlayers.value = players
  6. }
  7. }


下面是TwoPlayerGame Composable:

  1. @Composable
  2. fun TwoPlayerGame(navController: NavController, viewModel: MainViewModel) {
  3. val howManyPlayers by viewModel.howManyPlayers.collectAsState()
  4. Column {
  5. Row(Modifier.padding(50.dp)) {
  6. Text(text = "Two Player Game")
  7. }
  8. Row {
  9. Text(text = "Value of howManyPlayers in viewmodel = ${howManyPlayers}")
  10. }
  11. }
  12. }


当用户在PlayerSelection屏幕中选择多少个玩家时,MainViewModel中的HowManyPlayers变量会根据用户按下的按钮成功更改。我可以看到这一行的工作情况:
第一个月
这很酷。然而,当我点击按钮导航到TwoPlayerGame时,MainViewModel中的变量HowManyPlayers再次重置为0。为什么在导航到新屏幕时,MainViewModel中的HowManyPlayers值重置为0?是否有一种方法可以将ViewModel传递到不同的屏幕,同时保留传递的ViewModel中的变量值?
谢谢你考虑我的问题,我真的很感激!

mzsu5hc0

mzsu5hc01#

有两种方法可以共享数据。
首先,你可以通过使用导航参数来传递参数。传递一些值并在屏幕上获取值。https://developer.android.com/jetpack/compose/navigation#nav-with-args
第二,使用存储库层来共享一些数据。在存储库中公开一个流,并在ViewModel中收集此值。然后,通过存储库中的流发出一些数据。https://developer.android.com/topic/architecture/data-layer
也许,你可以通过修改NavigationAppHost函数来传递viewModel,而不是像下面这样创建新的示例,来与一个ViewModel共享数据。

  1. @Composable
  2. fun NavigationAppHost (navController: NavController, viewModel: MainViewModel) {
  3. NavHost(navController = navController as NavHostController, startDestination = "PlayerSelection") {
  4. composable(Destination.PlayerSelection.route) { PlayerSelection(navController, viewModel) }
  5. composable(Destination.TwoPlayerGame.route) {TwoPlayerGame(navController, viewModel)}
  6. composable(Destination.ThreePlayerGame.route) {ThreePlayerGame(navController, viewModel)}
  7. composable(Destination.FourPlayerGame.route) {FourPlayerGame(navController, viewModel)}
  8. }
  9. }

字符串
AAC(Android架构组件)建议每个屏幕使用一个ViewModel。因为它保存了特定的UI状态,当配置发生更改时可以被销毁。但是,MVVM模式还有另一种观点,比如在多个屏幕上重用ViewModel。您可以引用AAC ViewModelMVVM ViewModel
这里有很好的例子来学习ViewModel和导航与Jetpack Compose. https://github.com/android/socialite
https://github.com/android/nowinandroid [高级]

展开查看全部

相关问题