ViewModelProviders 在我的 Fragment 中不起作用

Posted

技术标签:

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

这就是我想要做的。

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

所以,我所做的是以下。

首先我创建了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 调用?

有什么线索吗?

谢谢。

【问题讨论】:

【参考方案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() 将返回刚刚创建的活动

【讨论】:

以上是关于ViewModelProviders 在我的 Fragment 中不起作用的主要内容,如果未能解决你的问题,请参考以下文章

无法解析 AppCompatActivity 上的符号 ViewModelProviders

ViewModelProviders 在 1.1.0 中已弃用

由于不推荐使用 ViewModelProviders.of(),我应该如何创建 ViewModel 的对象?

cakephp 4 中的 I18n::setLocale('en_FR') 是不是正确?

FatFS - 无法格式化驱动器,FR_MKFS_ABORTED

基于两列的 seaborn 范围背景