当前位置: 首页 > 知识库问答 >
问题:

android - 如何在Jetpack Compose中从Composable函数中直接获取ViewModel?

罗甫
2024-09-05
@HiltAndroidApp
class App: Application() { ... }
@AndroidEntryPoint
class MainActivity: ComponentActivity() {
    val vm: CustomViewModel = viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(....)
        setContent {
            AppTheme {
                CustomScreen(vm = vm)
            }
        }
    }
}
@HiltViewModel
class CustomViewModel: ViewModel() { ... }
@Composable
fun CustomScreen(vm: CustomViewModel) { ... }

从MainActiviy用viewModels()方法获取CustomViewModel后传给CustomScreen没有问题,但是从CustomScreen用viewModel()方法获取CustomViewModel就会报错:

@AndroidEntryPoint
class MainActivity: ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(....)
        setContent {
            AppTheme {
                CustomScreen()
            }
        }
    }
}
@Composable
fun CustomScreen(vm: CustomViewModel = viewModel()) { ... }

希望能从composable fun中直接获取CustomViewModel

共有1个答案

墨翔宇
2024-09-05

在 Jetpack Compose 中,你不能直接从 Composable 函数内部直接使用 viewModel() 函数,因为 viewModel() 是一个扩展函数,它依赖于当前的 LifecycleOwner(如 FragmentActivity),而 Composable 函数本身并不直接持有或管理生命周期。不过,你可以通过几种方式在 Composable 函数中访问 ViewModel。

方法 1: 通过参数传递 ViewModel

你已经展示了这种方法,即通过 Activity 或 Fragment 初始化 ViewModel,并通过参数传递给 Composable 函数。这是最常见和推荐的方式,因为它保持了 Compose UI 的声明性和无状态性。

方法 2: 使用 LocalObservation

对于更复杂的场景,如果你需要在多个 Composable 中共享 ViewModel 而不显式传递它,你可以考虑使用 LocalObservation(这是 Jetpack Compose 的一部分,但主要用于响应式数据流,而非直接用于 ViewModel)。然而,对于 ViewModel,更直接的方法(如方法 1)通常是更好的选择。

方法 3: 自定义 Compose 委托

虽然不常见,但你可以通过创建一个自定义的 Compose 委托来包装对 ViewModel 的访问。然而,这通常会增加复杂性,并且不如直接传递 ViewModel 作为参数那样清晰和直接。

方法 4: 使用 Hilt 或其他 DI 框架的 Compose 集成

如果你使用 Hilt 或其他依赖注入框架,并且该框架支持 Jetpack Compose,你可以尝试使用这些框架提供的特定 Compose 集成来在 Composable 函数中注入 ViewModel。然而,对于 Hilt,通常的做法仍然是通过 Activity 或 Fragment 注入 ViewModel,并将其作为参数传递给 Composable。

结论

在你的情况下,最简单和最直接的方法是继续使用 viewModels() 在 Activity 或 Fragment 中获取 ViewModel,并将其作为参数传递给 Composable 函数。这保持了代码的清晰性和可维护性,同时避免了在 Composable 函数中处理生命周期和依赖注入的复杂性。

@AndroidEntryPoint
class MainActivity: ComponentActivity() {
    private val vm: CustomViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            AppTheme {
                CustomScreen(vm = vm)
            }
        }
    }
}

@Composable
fun CustomScreen(vm: CustomViewModel) {
    // 使用 vm
}
 类似资料:
  • 我想制作一个简单的共享按钮来获取TextView值并共享它,但我不知道怎么做。 代码如下: 而相关的布局是: 我在这里看了文档和一些类似的问题,但找不到我的答案。感谢你对此的帮助。

  • 问题内容: 我有这样的网址: 如何获取章节和帖子的参数值​​? 我的URL在Chapter参数的值中包含“&”。 问题答案: 您可以在Android中使用Uri类来完成此操作; https://developer.android.com/reference/android/net/Uri.html 这样,您甚至可以从查询参数中获取特定元素;

  • 但我的问题是:我不知道如何在没有Cordova活动的情况下用java在后台服务中获得这些数据(“值”)。 Backgroud services Java:

  • 问题内容: 我正在尝试编写一个简单的Python脚本,它将所有子目录中的index.tpl复制到index.html(有一些例外)。 通过尝试获取子目录列表,我陷入了困境。 问题答案: 我对各种功能进行了 速度测试 ,以将 完整路径 返回到所有当前子目录。 tl; dr: 始终使用: 奖励:使用,您也可以只使用而不是来获取文件夹名称。 该函数(以及下面的所有其他函数)将不会使用 自然排序 。这意味

  • 问题内容: 给定一个函数,可以得到它的名字吗?说: 有人告诉我runtime.FuncForPC会有所帮助,但我不明白如何使用它。 问题答案: 很抱歉回答我自己的问题,但我找到了解决方案: