尝试使用 Jetpack Compose 了解 ViewModel 和导航

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

我试图了解如何使用 ViewModel 来保存可以传递到不同屏幕的值。

我编写了一个非常简单的程序来尝试理解这个概念。

这是我的主要活动:

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())}
    }


}

这是我的屏幕设置:


@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
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
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 屏幕中选择多少个玩家时,MainViewModel 中的 HowManyPlayers 变量会根据用户按下的按钮而成功更改。我可以看到这与以下行一起工作:

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

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

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

android kotlin navigation android-jetpack-compose android-viewmodel
1个回答
2
投票

有两种共享数据的方式。

首先,您可以使用导航参数来传递参数。传递一些值并在屏幕上获取值。 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 Architecture Component)建议每个屏幕使用一个 ViewModel。因为它保存特定的 UI 状态,当发生配置更改时可以销毁该状态。但是,MVVM 模式还有另一种观点,例如在多个屏幕上重用 ViewModel。 您可以参考AAC ViewModelMVVM 视图模型

以下是使用 Jetpack Compose 学习 ViewModel 和导航的好示例。 https://github.com/android/socialite
https://github.com/android/nowinandroid [高级]

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