android Jetpack compose中的活动视图模型

n7taea2i  于 2023-05-15  发布在  Android
关注(0)|答案(4)|浏览(119)

在片段中,我们有

private val activityViewModel: MainActivityViewModel by activityViewModels()
private val fragmentViewModel: MainFragmentViewModel by viewModels()

以获得整个应用程序中的共享视图模型(活动视图模型)和视图特定视图模型(片段视图模型)的示例。
我是来作曲的。
如何在jetpack compose中获得两个不同范围的视图模型?
从文件上,我可以看到这一行,
viewModel()返回现有的ViewModel或在给定范围内创建新的ViewModel。
但是,我如何指定视图模型的范围呢?
附言
我已经通过this question,这是类似的,但它没有任何答案。

toe95027

toe950271#

通常在一个复合树中,比如在setContent内容中,有一个视图模型范围在所有子复合之间共享。
如果需要,可以使用LocalViewModelStoreOwner覆盖它:

CompositionLocalProvider(
    LocalViewModelStoreOwner provides viewModelStoreOwner
) {
    NextComposable()
}

“合成导航”将覆盖每个导航目的地的“合成导航”。请参阅this answer以了解如何在导航目的地之间共享它。

zzwlnbp8

zzwlnbp82#

详细说明@Pylyp Dukhov。由于可以直接在树中更改默认提供程序,因此可以创建一个函数来获取特定ViewModelStore中的viewModel
我已经为此做了一个gist
内容如下:

/** Try to fetch a viewModel in [store] */
@Composable
inline fun <reified T : ViewModel, S : ViewModelStoreOwner> viewModelInStore(store: S): Result<T> =
    runCatching {
        var result: Result<T>? = null
        CompositionLocalProvider(LocalViewModelStoreOwner provides store) {
            result = runCatching { viewModel(T::class.java) }
        }
        result!!.getOrThrow()
    }

/** Try to fetch a viewModel with current context (i.e. activity)  */
@Composable
inline fun <reified T : ViewModel> safeActivityViewModel(): Result<T> = runCatching {
    val activity = LocalContext.current as? ViewModelStoreOwner
        ?: throw IllegalStateException("Current context is not a viewModelStoreOwner.")
    return viewModelInStore(activity)
}

/** Force fetch a viewModel inside context's viewModelStore */
@Composable
inline fun <reified T : ViewModel> activityViewModel(): T = safeActivityViewModel<T>().getOrThrow()

在活动存储上获取viewModel与从默认树的存储中获取viewModel一样简单。

@Composable
fun MyComposeElement(
    fragmentViewModel: ComposeViewModel = viewModel(),
    activityViewModel: ComposeViewModel = activityViewModel()
) {
    assert(fragmentViewModel != activityViewModel)
    assert(fragmentViewModel == viewModel<ComposeViewModel>())
    assert(activityViewModel == activityViewModel<ComposeViewModel>())
}
x0fgdtte

x0fgdtte3#

class MainViewModel: ViewModel() { }

@Composable
fun MainScreen() {
    // ViewModel with a scope of the current class.
    val screenViewModel = viewModel<MainViewModel>()
    
    // ViewModel with a scope of the activity.
    val activity = LocalContext.current.getActivity()
    activity?.let {
        val activityViewModel = viewModel<MainViewModel>(viewModelStoreOwner = activity)
    }
}

请确保导入合成视图模型。

import androidx.lifecycle.viewmodel.compose.viewModel

另外,getActivity()函数不能直接使用,请使用下面的Context扩展。参考:https://stackoverflow.com/a/67927037/6512100

fun Context.getActivity(): AppCompatActivity? {
    var context = this

    while (context is ContextWrapper) {
        if (context is AppCompatActivity) return context

        context = context.baseContext
    }

    return null
}
2ic8powd

2ic8powd4#

根据这个链接
例如,如果我有一个settings导航图,我可以导航到theme,如果我想要theme使用settingsViewModel,那么我可以通过以下方式实现这一点:

private fun NavGraphBuilder.theme(
    navController: NavController,
) {
    composable(route = MainSubScreen.Theme.route) {
        val settingsViewModel = hiltViewModel<SettingsViewModel>(
            navController.getBackStackEntry(MainSubScreen.Settings.route)
        )
        Theme(
            settingsViewModel = settingsViewModel,
            backToSettings = { navController.popBackStack() },
        )
    }
}

或者,如果你想让theme使用settingsViewModel,那么这个范围就是theme本身。你可以像下面这样做,或者直接在你的theme可组合函数中创建一个settingsViewModel示例,而不用像这样传递参数。如果你这样做,你会注意到当你从theme按回settings时,settingsViewModel内部的onCleared将被调用。

private fun NavGraphBuilder.theme(
    navController: NavController,
) {
    composable(route = MainSubScreen.Theme.route) {
        val settingsViewModel = hiltViewModel<SettingsViewModel>()
        Theme(
            settingsViewModel = settingsViewModel,
            backToSettings = { navController.popBackStack() },
        )
    }
}

相关问题