这就是我正在努力实现的目标(注意,我对Jetpack Compose还很陌生):
Jetpack创作于2023年4月。日期很重要,因为我正在寻找最新的最佳练习。我试过很多不同的例子,但它们要么不起作用,要么现在被认为是糟糕的做法。
用例:
- MainActivity:执行任何基本设置,然后使用NavHost打开第一个名为"主页"的页面
- 主页有多个按钮,可以关闭并执行不同的任务
- 这些任务中的第一个(此处仅考虑一个)是使用共享ViewModel的多页表单(多页)。我们称它们为"FormPageViewModel"、"FormPage1"、"FormPage2"one_answers"FormPage3">
- 所以在主页上,我点击了;"创建表单"按钮,我现在正在查看FormPage1。现在在那里我可以做一些我认为是:
// Create a new one or find one that already exist (it shouldn’t at this point for note).
val formPageViewModel = ViewModel<FormPageViewModel>()
- 现在我来回浏览我的表单页面,填写内容。我可以点击取消按钮(甚至是FormPage1中的后退按钮)返回主页,也可以在最后一页上点击按钮完成表单,然后将其保存到json(此处不相关),然后返回主页
当我返回主页时,我正在努力摆脱FormPageViewModel中的"销毁/删除/删除/你的首选术语"。基本上,它应该像从未被实例化一样,只有当我们再次打开FormPage1时才会被实例化。
现在,正如我在上面已经说过的,我已经尝试了很多方法,从那些似乎不再有效或被认为是不良做法的例子中。我讨论过创建两个活动,但即使这样似乎也不受欢迎(尽管它确实有效)。
我也可以在"活动"中创建ViewModel,并将其与导航代码一起传递给可组合组件,甚至可能销毁ViewModel,但这似乎非常混乱,而且在我阅读的一些地方似乎再次遭到反对。
我现在也在努力避免Hilt和DI,直到我从一个原始的角度了解如何做到这一点以及在添加更多抽象之前发生了什么。
在compose中,您需要像在生命周期为"活动"或"片段"的视图中思考一样进行思考,这些视图可以通过委托使用activityViewModels
或viewModels
附加视图模型,在compose中,可组合的生命周期是它进入组合、重新组合和退出组合的时间。
这是基于事件/状态的,您需要将实例保存在组合发生一次的位置,以防止实例重复。
当你创建NavigationGraph
时,你将只创建一次这个可组合的,然后你可以在它里面创建你的视图模型,并将它传递给你需要这个视图模型的每个composable(route)
函数。
在compose中,我们可以使用一种名为DisposableEffect
的东西,它将在onDispose中触发(当composeable离开compose时)
此时,当您返回时,您可以清除此可组合文件保存在视图模型中的所有数据。但您可以为它们保留相同的实例。
你可以在这里阅读https://developer.android.com/jetpack/compose/side-effects#disposableeffect
伪代码
NavigationGraph(navController: NavigationController) {
val viewmodel: SharedViewmodel = viewModel()
NavHost(...) {
composable(route1) {
YourFirstComposable(viewmodel)
}
composable(route2) {
YourSecondComposable(viewmodel)
}
}
在使用此视图模型的每个目的地内,使用DisposableEffect
清除视图模型内的数据,进行清理等。
正如您在上面看到的,没有必要将sharedViewmodel
范围界定为您的活动。
我最近也遇到了同样的挑战,我想我终于找到了创建多页表单的方法,同时使用DI(hilt),并使表单在页面之间使用相同的视图模型。
你需要阅读嵌套的NavGraphs
,并意识到你可以有几个,甚至可以把它们放在Scaffold
中(例如,这样你就可以保留一个给定的top
或bottomBar
,只需浏览Scaffold
的content
部分)。其结果类似于我们过去在旧的声明性UI系统中使用Fragments所实现的结果。
在我的情况下,我在MainActivity
中托管我的主NavGraph
,如下所示:
setContent {
MyTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = Screen.EntryScreen.route
) {
composable(route = Screen.EntryScreen.route) {
EntryScreen(navController = navController)
}
composable(route = Screen.PublishLot.route) {
PublishLot(mainNavController = navcontroller) //this is where the form is
}
}
}
}
}
现在一个峰值进入我的PublishLot
多页表单@Composable
@Composable
fun PublishLot(
mainNavController: NavHostController,
viewModel: PublishLotViewModel = hiltViewModel()
) {
val navController: NavHostController= rememberNavController()
Scaffold(topBar = {
CenterAlignedTopAppBar(title = {
Text(text = "Publish Lot")
})
}) { padding ->
Surface(modifier = Modifier.padding(padding)) {
NavHost(
navController = navController,
startDestination = "form/page1"
) {
composable(route = "form/page1") {
Page1(navController = navController, viewModel = viewModel)
}
composable(route = "form/page2") {
Page2(navController = navController, viewModel = viewModel, doOnFinished ={mainNavController.popBackStack()})
}
}
}
}
}
正如您所看到的,我只是在PublishLot
表单Scaffold
的内容中创建一个新的NavGraph
,并实例化一个新NavController
(这很重要,它不能与主NavGraph
中的相同NavController
一起使用)。
请注意,我使用hilt将viewModel注入PublishLot
,但随后将其显式传递给表单中的两个页面。这意味着我的viewModel中的状态不受页面导航的影响。按下"后退"按钮会像人们所期望的那样进入表单中的上一页。
我仍在将mainNavController
传递给可组合的PublishLot
,以便在填写表单后将backStack
弹出,从而消除为我创建的PublishLotViewModel
实例。
让我知道如果这满足你的要求