【问题标题】:ViewModels creation is not supported in Preview预览版不支持创建 ViewModel
【发布时间】:2022-05-01 03:38:07
【问题描述】:

我按照官方guide 创建了viewModel 实例,它运行良好。但是,当@composable 中有任何viewModel 时,Android Studio 将无法呈现预览并且错误代码为ViewModels creation is not supported in Preview。有人有解决办法吗?

附:使用撰写版本1.0.0-alpha06

【问题讨论】:

  • 目前预览可以通过 func 参数传递 viewModel,但是如果我有很多 viewmodel 就很奇怪
  • @Preview 主要用于可组合层次结构的末端(例如,列表中的行),这些可组合对象既不接收也不实例化视图模型。相反,它们应该接收普通参数、State 对象和用于回调的 lambda——您可以轻松地在可组合声明中为其提供默认值。
  • 我可以看到一些在测试时使用mock。想知道使用预览时是否有类似的事情。如果任何子元素可使用任何视图模型进行组合,则无法预览父屏幕会很不方便
  • @CommonsWare 不符合此文档。可组合项可以采用视图模型。我认为Android团队只需要添加对它的支持:developer.android.com/jetpack/compose/state
  • 我遇到了同样的问题。我的解决方法是让我的 viewModel 可以为空并为预览提供默认值。

标签: android-jetpack-compose


【解决方案1】:

您可以使用如下所示的方法,该方法将显示在下面推荐的视频中:

@Composable
fun TestView(
    action: MainActions,
    viewModel: OnboardViewModel = getViewModel()
) {
    TestUI(onClick = viewModel.clickMethod())
}

@Composable
fun TestUI(onClick: () -> Unit) {}

@Preview
@Composable
fun TestUIPreview() {
    MaterialTheme() {
        TestUI(onClick = {})
    }
}

在所选时间,此视频中有来自 google 的推荐:https://youtu.be/0z_dwBGQQWQ?t=573

【讨论】:

  • 我正在使用刀柄,当我使用 = hiltViewModel() 时,它仍然显示:ViewModels creation is not supported in Preview
  • 这个例子试图展示你的 UI 和 ViewModel 解耦的情况。 viewmodel 与 UI 对话的唯一方式是通过方法引用。因此,如果您收到此错误是因为您的 ViewModel 位于不应出现的 @Preview 中。
  • @Dr.jacky 你找到解决方案了吗?
  • 他正在以一种不应该做的方式做这件事。您无法在预览中初始化视图模型
【解决方案2】:

我遇到了完全相同的问题。 解决方案是:使用接口扩展 ViewModel

撰写视图:

@Composable
fun MyScreen(myVm: IMyViewModel = MyViewModel()) {
    Text(text = myVm.getTextA())
}


@Preview()
@Composable
fun MyScreenPreview() {
    MyScreen(myVm = MyViewModelPreview())
}

视图模型:

abstract class IMyViewModel : ViewModel(){
    abstract val dynamicValue: StateFlow<String>
    abstract fun getTextA() : String
}

class MyViewModel : IMyViewModel() {
    private val _dynamicValue: MutableStateFlow<String> = MutableStateFlow("")
    override val dynamicValue: StateFlow<String> = _dynamicValue

    init {
    }

    override fun getTextA(): String {
        return "Details: ${EntityDb.getAllEntities().lastOrNull()?.details}"
    }
}

class MyViewModelPreview(override val dynamicValue: StateFlow<String> =    MutableStateFlow("no data")) : IMyViewModel() {
    override fun getTextA(): String {
        return ""
    }
}

【讨论】:

    【解决方案3】:

    您的所有可组合项都必须是无状态的,当涉及到 ViewModel 时,最好将 viewModel 向下传递。

    // Base viewModel contract
    interface MainViewModel {
         fun getData(): Data
    }
    
    // Real viewModel to use in production
    class RealViewModel : MainViewModel {
          override fun getData(): Data = getRealData()
    }
    
    // Fake viewModel to use in Previews and Tests 
    object FakeViewModel : MainVeiwModel {
          override fun getData(): Data = getFakeData()
    }
    
    @Composable
    fun Composable(viewModel: MainViewModel) {
        ShowData(viewModel.getData())
    }
    
    @Preview
    @Composable
    fun ComposablePreview() {
        Composable(FakeViewModel)
    }
    

    您还可以拥有一个复杂的ViewModel,它有大量的依赖项将被注入到某个地方。在您的层次结构的顶层创建您的 ViewModel 实例,因为它是有意义的。

    【讨论】:

      【解决方案4】:

      你可以使用接口和hilt

      interface IMyViewModel {
      
          fun getTextA() : String
      
      }
      
      @HiltViewModel
      class MyViewModel() : ViewModel(), IMyViewModel {
          fun getTextA() : String {
              //do some cool stuff
          }
      }
      
      class MyViewModelPreview() : IMyViewModel {
          fun getTextA() : String {
              //do some mock stuff
          }
      }
      
      @Composable
      fun MyScreen(myVm = hiltViewModel<MyViewModel>()) {
          Text(text = myVm.getTextA())
      }
      
      
      @Preview()
      @Composable
      fun MyScreenPreview() {
          MyScreen(myVm = MyViewModelPreview())
      }
      

      在这一点上,MyViewModel 是一个使用 @HiltViewModel 注释的 IMyViewModel 的实现,hiltViewModel 为您完成所有必需的连接,在预览中您可以使用任何其他简单的模拟实现。

      如果您需要为您的视图模型提供一些依赖项,请使用带有匕首注入的注入构造函数(hilt 已经支持)。显然,这种依赖关系应该在您的实际视图模型上进行调整,并且您的预览实现需要只是一个没有其他依赖关系的包装类,因为它们的功能只是满足参数

      @HiltViewModel
      class MyViewModel @Inject constructor(
          private val myDependencyRepositoryOne: MyDependencyRepositoryOne,
          private val myDependencyRepositoryTwo: MyDependencyRepositoryTwo)
      : ViewModel(), IMyViewModel {
          fun getTextA() : String {
              //do some cool stuff
          }
      }
      

      Here 是另一个与 compose 中的视图模型注入相关的有用资源

      【讨论】:

      • 如果我在 viewModel 中有大量依赖项怎么办?
      • 您可以使用注入的构造函数,因为刀柄是在匕首上构建的。我编辑了我的答案以显示如何。
      【解决方案5】:

      我正在使用什么:

        @Preview(showBackground = true)
          @Composable
          fun DefaultPreview() {
               MyMVIApp1Theme {
                 val myViewModel = remember { AppViewModel() }
                 Greeting(viewModel = myViewModel,"Android")
               }
          }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-06-14
        • 2015-08-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-08-18
        • 1970-01-01
        • 2023-03-05
        相关资源
        最近更新 更多