Android:清除 DialogFragment ViewModel

Android: Clear 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()[=21 之前保存的数据=]

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

确保您使用的是片段的视图模型。您不应通过将值发布到实时数据来清除视图模型 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>()

Adrian 好心地向我提供了他的项目样本。本答案基于调试示例项目的结果。

导致您遇到的问题实际上位于您的 MainActivity class 中。您在 activity 的 onCreate 方法中实例化 AddReminderFragment,当按下 FAB 时,您将显示此片段的实例。

一切似乎都很好,除了 - 你总是呈现 AddReminderFragment 的相同实例。这意味着当你第一次展示这个片段时,你会得到全新的 AddReminderViewModel,它是使用 by viewModels().

延迟加载的

最初,我认为延迟加载 by viewModels() 导致了这个问题,因为它使用片段本身作为视图模型存储并且 它缓存创建的视图模型 以防万一您稍后会想要重用它。事实并非如此。

如何解决这个问题?

永远不要存储对活动和片段的引用,或者尽可能避免,当你有任何引用时要小心。它可能会导致内存泄漏。

简要描述我做了什么:

  1. MainActivity 中删除了 private lateinit var modal: AddReminderFragment 变量;
  2. MainActivity 中删除了 ITimePipupCallback
  3. 已从 TimePopupFragment 中删除 onAttach(context: Context)
  4. 已在 TimePopupFragment 中实施 fun setCallback(callback: ITimePipupCallback): TimePopupFragment
  5. 在显示来自 AddReminderFragmentTimePopupFragment 实例之前,传入回调 ITimePipupCallback 实例。就好像它是对 AlertDialog;
  6. 按钮点击的回调
  7. AddReminderFragment 中删除了 onTimePickedonDayPicked 方法,因为它们不再需要。

已更新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
    }

    ...

}

您可以安全地从 AddReminderFragment 中删除 override fun onDismiss(dialog: DialogInterface)