Android:清除DialogFragment ViewModel 如何解决该问题?

问题描述

我正在创建一个扩展BottomSheetDialogFragment的对话框。在此对话框中,我有一个viewmodel来保存片段的状态(它内部有一些输入)。 viewmodel的某些属性LiveData。我注意到,在关闭对话框(调用dismiss()或在对话框外部点击)并重新打开它时,LiveData属性的值会保持不变。

因此,作为解决方案,我想重写ondismiss()事件以手动清除viewmodel中的数据:

 override fun ondismiss(dialog: DialogInterface) {
//        Clear the viewmodel
        viewmodel.clear()
        viewmodelStore.clear()

        super.ondismiss(dialog)
//        Unsubscribe from observer
        viewmodel.getContact().removeObservers(viewLifecycleOwner)

    }

viewmodel.clear()是我的viewmodel中的一个函数,用于将内部数据重置为认值。

显然,这还是不够的,因为即使在解雇之前在viewmodel中重置了数据之后,在重新打开对话框时,viewmodel仍保留了它在调用clear()

之前保存的数据。

由于不是LiveData属性会重置,因此我相信有一种重置LiveData值的特定方法,但是我不知道该怎么做。谁能帮我吗?预先感谢!

解决方法

阿德里安(Adrian)向我提供了他的项目的样本。该答案基于调试示例项目的结果。

导致您遇到问题的问题实际上位于您的[{'time': '2020-08-21T00:00:00.000Z','click': 3},...] 类中。您可以在活动的MainActivity方法中实例化AddReminderFragment,并在按下FAB时显示此片段的实例。

一切似乎都不错,除了-您总是展示相同的onCreate 实例。这意味着,当您首次显示此片段时,您会获得全新的AddReminderFragment,该文件是使用AddReminderViewModel延迟加载的。

最初,我认为延迟加载by viewModels()导致了此问题,因为它使用片段本身作为视图模型存储,并且它缓存创建的视图模型,以防您想重复使用以后再说。情况并非如此。

如何解决该问题?

切勿存储对活动和片段的引用,或尽其所能避免,如有任何注意,请多加注意。它可能会导致内存泄漏。

简述我做了什么:

  1. by viewModels()中删除了private lateinit var modal: AddReminderFragment变量;
  2. MainActivity中删除了ITimePipupCallback
  3. MainActivity中删除了onAttach(context: Context)
  4. TimePopupFragment中实现了fun setCallback(callback: ITimePipupCallback): TimePopupFragment
  5. 在显示通过回调TimePopupFragment实例传递的TimePopupFragment的{​​{1}}实例之前。好像是对AddReminderFragment的按钮单击的回调;
  6. ITimePipupCallback中删除了AlertDialogonTimePicked方法,因为它们不再需要。

更新了onDayPicked

AddReminderFragment

MainActivity的更新:

class MainActivity : AppCompatActivity() {

    companion object {
        const val AddReminderModalTag = "add_reminder_modal"
    }

    private lateinit var binding: MainActivityBinding
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = MainActivityBinding.inflate(layoutInflater)
        binding.showModalHandler = this
        setContentView(binding.root)
    }

    fun showAddReminderModal(view: View) {
        AddReminderFragment.newInstance().show(supportFragmentManager,AddReminderModalTag)
    }
}

AddReminderFragment的更新:

class AddReminderFragment : BottomSheetDialogFragment() {
    ...
    override fun onCreateView(
        inflater: LayoutInflater,container: ViewGroup?,savedInstanceState: Bundle?
    ): View? {
        ...
        
        //Bind the add time button
        binding.addTimeButton.setOnClickListener {
            TimePopupFragment
                .newInstance(viewModel.getReminderType().value!!)
                .setCallback(object : ITimePipupCallback {
                    override fun onTimePicked(hour: Int,minute: Int) {
                        viewModel.addTimeToRemind(hour,minute)
                    }

                    override fun onDayPicked(day: Int) {
                        viewModel.addTimeToRemind(day)
                    }
                })
                .show(
                    parentFragmentManager,TIME_PICKER_TAG
                )
        }

        ...

        return binding.root
    }

    ...
}

您可以从TimePopupFragment中安全删除class TimePopupFragment : DialogFragment() { ... private lateinit var callback: ITimePipupCallback // Removed `onAttach` method fun setCallback(callback: ITimePipupCallback): TimePopupFragment { this.callback = callback return this } ... }

,

确保您正在使用片段的视图模型。您不应通过将值发布到实时数据postValue(T value)中来清除视图模型,因为这是不必要的。

使用以下视图模型,

class MyViewModel : ViewModel() {

    private val users: MutableLiveData<List<User>> by lazy {
        MutableLiveData().also {
            loadUsers()
            }
        }

    fun getUsers(): LiveData<List<User>> {
        return users
    }

    private fun loadUsers() {
        // Do an asynchronous operation to fetch users.
    }
}

在DialogFragment中,通过以下方式初始化视图模型

ViewModelProviders.of(<FRAGMENTT>).get(MyViewModel.class);

如果您使用的是ktx,则通过以下方式初始化视图模型

// Get a reference to the ViewModel scoped to this Fragment
val viewModel by viewModels<MyViewModel>()