【问题标题】:ViewModelProviders is not working inside my FragmentViewModelProviders 在我的 Fragment 中不起作用
【发布时间】:2019-11-10 15:42:49
【问题描述】:

这就是我想要做的。

  • 在 Fragment 内设置对象的 ArrayList
  • FragmentActivity 中的观察者那里获取该数组 容器(承载所有片段的活动)

所以,我所做的是以下。

首先我创建了SharedViewModel,我将从那里设置和获取数据:

SharedViewModel.kt

class SharedViewModel: ViewModel() {

    var personArrayObj:MutableLiveData<ArrayList<Person>> = MutableLiveData()

    fun setPersonArray(personArray:ArrayList<Person>){
        personArrayObj.value = personArray
    }

    val getPersonArray:LiveData<ArrayList<Person>>
    get() = personArrayObj

}

现在,我在托管所有片段创建的MainActivity 中实例化这个ViewModel,所以,我可以从片段集的任何位置获取ArrayData

MainActivity.kt

class MainActivity: FragmentActivity(){

private lateinit var viewModel:SharedViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        viewModel = ViewModelProviders.of(this).get(SharedViewModel::class.java)
        viewModel.getPersonArray.observe(this, Observer { it:ArrayList<Person>
            Log.d("Array:",""+it)
        })
    }

}

然后我需要做的最后一件事是从我想要的任何 Fragment 设置该 Array 的值,然后如果值发生更改,将触发主机 Activity,所以,要做到这一点,我简单地做一个 @ 987654331@ 在我想要的 Fragment 上。

问题

当我尝试在任何片段中实例化 SharedViewModel 时,我遇到了这个问题:

我试图断言!对于 null,也可以使用 let,但它不起作用,我想知道 FragmentActivity 是从哪里来的

如果我使用 viewLifecycleOwner 而不是 activity 它告诉我它不能用 FragmentFragmentActivity 调用?

有什么线索吗?

谢谢。

【问题讨论】:

    标签: android kotlin mvvm fragment android-viewmodel


    【解决方案1】:

    Fragments 提供了一个辅助方法来检索非空 Activity - requireActivity()。用它代替activity

    viewModel = ViewModelProviders.of(requireActivity()).get(SharedViewModel::class.java)
    

    作为替代方案,您可以在应用中包含 fragment-ktx 依赖项并使用 by activityViewModels() Kotlin 属性扩展而不是使用 ViewModelProviders.of()

    val viewModel: SharedViewModel by activityViewModels()
    

    【讨论】:

    • 使用requireActivity() 或多或少等同于使用activity!!。应用程序将崩溃。在IllegalStateException 或 NPE 上。
    • @MarošŠeleng - 在这种情况下,来自诸如onViewCreated() 之类的生命周期方法,它永远不会崩溃,因此无论您抑制lint 警告,使用!! 还是使用requireActivity()您将获得所有三个相同的运行时行为。
    • 确实如此,但您在回答中没有提及任何这些,一般而言,这是一种不好的做法。我想我们都知道,Fragment 的生命周期到底有多大,为什么不一直把它写成安全的呢?
    • @MarošŠeleng - requireActivity()onViewCreated() 中绝对安全,这就是为什么我的回答指出,当您作为开发人员时,它是获取非空活动实例的正确方法,可以保证activity必须是非空的。
    • 你能指导我@ianhanniballake 我最新的问题吗?谢谢
    【解决方案2】:

    ViewModelProviders.of() 已被弃用。所以,我们可以像下面这样初始化视图模型。

     viewModel = ViewModelProvider(requireActivity()).get(SampleViewModel::class.java)
    

    【讨论】:

      【解决方案3】:

      好的,我没看到

      我在我的 Fragment 中创建了一个 viewModel 实例,在我的 MainActivity 中创建了另一个实例,所以这样做,我们会阻止观察者工作。

      为了解决这个问题,我只是做了一个简单的方法,它返回我在MainActivity 中创建的实例

       fun getViewModelInstance():SharedViewModel = viewModel
      

      然后我只需从任何 Fragment 调用该实例来使用它

      (activity as MainActivity).getViewModelInstance().setPersonArray(personArray)
      

      【讨论】:

      • 当您使用ViewModelProvider.of() 并传入您的活动时,您将获得与活动中完全相同的实例,因此您的想法是正确的。只有当您从 Fragment 中传入 this 时,您才能创建一个与 Fragment 相关联的全新 ViewModel。
      • 你不想那样做,如果你想用和activity中一样的,就用activity scope吧。
      • 非常有帮助。也被困在这里。谢谢极客!
      【解决方案4】:

      基本上,问题是在您的片段类中 activity context 将是 nullable 并且 ViewModelPorviders 需要 Non-null context 才能使用.

      一种解决方案是让您的 ViewModel 对象 可为空 (因为您的活动是可为空的) 在您的片段中,如下所示:

      var viewModel: SharedViewModel? = null
      

      然后在初始化期间,

      viewModel = activity?.let { ViewModelProviders.of(it).get(SharedViewModel::class.java) }
      

      【讨论】:

        【解决方案5】:

        把东西放在 onActivityCreated() 而不是 onCreateView()... 这将解决 getActivity() 可为空的问题...... 因为根据一个fragment的生命周期,onActivityCreated()是在onCreareView()之后... 并且活动是在 onActivityCreated() 的调用上创建的...... 因此,getActivity() 将返回刚刚创建的活动

        【讨论】:

          猜你喜欢
          • 2019-02-27
          • 1970-01-01
          • 2023-04-03
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多