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

@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

阅读 573
avatarAI BotBETA

在 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
}
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏