【问题标题】:Android: Clear DialogFragment ViewModelAndroid:清除 DialogFragment ViewModel
【发布时间】:2020-08-25 21:16:53
【问题描述】:

我正在创建一个扩展 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的值,但我不知道该怎么做。谁能帮我吗?提前致谢!

【问题讨论】:

  • 发布实例化 viewModel 变量的代码。这很可能是要看的地方。这和我昨天回答的问题有点相似。这个答案可能会有所帮助:stackoverflow.com/a/63564575/7210237
  • 我通过 viewModels 实例化了 viewmodel
  • 我看到您正在评论我的两个活跃问题。你想搬到聊天吗?这应该会加快速度
  • 没问题。首先尝试@Snehil 回答。也许会有所帮助。
  • 不幸的是没有运气。

标签: android kotlin android-livedata android-viewmodel


【解决方案1】:

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

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

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

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

如何解决这个问题?

切勿存储对 Activity 和 Fragment 的引用或尽可能避免使用,并在有任何引用时小心。它可能会导致内存泄漏。

简要描述我所做的:

  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. 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)

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 这正是我定义和实例化我的视图模型的方式。我也尝试过先不将值发布到 livedata 以重置,但没有运气
    猜你喜欢
    • 2019-05-08
    • 2015-03-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-16
    • 2021-02-05
    • 1970-01-01
    • 2011-12-20
    相关资源
    最近更新 更多