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()
导致了这个问题,因为它使用片段本身作为视图模型存储并且 它缓存创建的视图模型 以防万一您稍后会想要重用它。事实并非如此。
如何解决这个问题?
永远不要存储对活动和片段的引用,或者尽可能避免,当你有任何引用时要小心。它可能会导致内存泄漏。
简要描述我做了什么:
- 从
MainActivity
中删除了 private lateinit var modal: AddReminderFragment
变量;
- 从
MainActivity
中删除了 ITimePipupCallback
;
- 已从
TimePopupFragment
中删除 onAttach(context: Context)
;
- 已在
TimePopupFragment
中实施 fun setCallback(callback: ITimePipupCallback): TimePopupFragment
;
- 在显示来自
AddReminderFragment
的 TimePopupFragment
实例之前,传入回调 ITimePipupCallback
实例。就好像它是对 AlertDialog
; 按钮点击的回调
- 从
AddReminderFragment
中删除了 onTimePicked
和 onDayPicked
方法,因为它们不再需要。
已更新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)
。
我正在创建一个扩展 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()
导致了这个问题,因为它使用片段本身作为视图模型存储并且 它缓存创建的视图模型 以防万一您稍后会想要重用它。事实并非如此。
如何解决这个问题?
永远不要存储对活动和片段的引用,或者尽可能避免,当你有任何引用时要小心。它可能会导致内存泄漏。
简要描述我做了什么:
- 从
MainActivity
中删除了private lateinit var modal: AddReminderFragment
变量; - 从
MainActivity
中删除了ITimePipupCallback
; - 已从
TimePopupFragment
中删除onAttach(context: Context)
; - 已在
TimePopupFragment
中实施fun setCallback(callback: ITimePipupCallback): TimePopupFragment
; - 在显示来自
AddReminderFragment
的TimePopupFragment
实例之前,传入回调ITimePipupCallback
实例。就好像它是对AlertDialog
; 按钮点击的回调
- 从
AddReminderFragment
中删除了onTimePicked
和onDayPicked
方法,因为它们不再需要。
已更新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)
。