android - DialogFragment 打开时未执行 RepeatOnLifecycle
问题描述
我有一个片段,它打开一个 DialogFragment,其中包含一些设置。当我更改 DialogFragment 中的设置时,我希望底层 Fragment 收集更改。我使用视图模型和 StateFlows 来存储设置并在 Fragment 中设置收集器,但是,它们永远不会被调用。但是视图模型已正确更新。是viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED)
因为 DialogFragment 正在显示而没有调用收集器吗?但是背景片段仍然处于活动状态。
当我使用MutableLiveData
代替MutableStateFlow
并制作观察者而不是收集器时,它可以工作。所以它一定和repeatOnLifecycle有关。
class SomeFragment : Fragment() {
// Dropped the other functions, e.g. onCreate, for simplicity
val settings by activityViewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.testButton.setOnClickListener {
val settingsFragment = SettingsDialogFragment()
settingsFragment.show(requireActivity().supportFragmentManager, "settings")
}
lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
settings.lowerState.collect {
Log.e("DEBUG", "lowerState switched")
}
settings.upperState.collect {
Log.e("DEBUG", "upperState switched")
}
}
}
}
}
class SettingsViewModel(application: Application) : AndroidViewModel(application) {
val lowerState: MutableStateFlow<Boolean> = MutableStateFlow(true)
val upperState: MutableStateFlow<Boolean> = MutableStateFlow(true)
}
对话框片段是一个没有特殊功能的简单对话框
class SettingsDialogFragment : DialogFragment() {
private var _binding: FragmentSettingsDialogBinding? = null
private val binding get() = _binding!!
private val settings: SettingsViewModel by activityViewModels()
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
_binding = FragmentRecordingSettingsDialogBinding.inflate(
layoutInflater,
null,
false
)
_binding!!.settings = settings
val builder = AlertDialog.Builder(requireActivity())
return builder.setView(binding.root).create()
}
}
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="settings"
type="com.example.SettingsViewModel"/>
</data>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<androidx.appcompat.widget.SwitchCompat
android:checked="@={settings.upperState}"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<androidx.appcompat.widget.SwitchCompat
android:checked="@={settings.lowerState}"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</layout>
解决方案
拿走了你的代码 - 只是我能够让它工作的准系统。您可以看到来自收集到的流的日志记录,这些记录SomeFragment
由开关更改:
想法:
我假设通过使用委托by activityViewModels()
来让你SettingsViewModel
的两个 Fragment 有一个共同的 parent Activity
,因此提供了相同的视图模型实例。
您的collect { .. }
尾电话正在暂停,因此您只会让第一个收集器收集,在这种情况下:
settingsViewModel.lowerState
.collect { .. suspending block in this coroutine nothing passed this will execute as FlowCollector still "collecting" and pushing into this block .. }
同suspend CoroutineScope.() -> Unit
一块中多个流的正确语法类似于:
settingsViewModel.lowerState
.onEach { Log.e("DEBUG", "lowerState switched") }
.launchIn(this)
settingsViewModel.upperState
.onEach { Log.e("DEBUG", "upperState switched") }
.launchIn(this)
这种方式launchIn()
是尾调用的捷径scope.launch { collect() }
- 这会启动一个没有suspend
块的新协程 - 由onEach { .. }
.
除此之外,除了可以使用您自己的准系统代码并尝试在一个新项目中将这个逻辑作为根本问题之外,没什么可说的。借助 Android 围绕范围/生命周期创建的所有“辅助”函数,它确实使该代码更难独立测试,因为它依赖于框架类和 LifeCycleOwner 回调,恕我直言,这使得它更难理解 - 但是..只是我的意见和一些我在复制和查看代码时想到了。
推荐阅读
- jquery - 点击链接按钮
- regex - 在变量值“52/80”中搜索子字符串“52”在 TCL 中不起作用
- arrays - qbasic 如何将数组和变量传递给函数/子程序?
- reactjs - 使用 Firebase 和 Cloud Firestore 进行身份验证
- ios - 如何为 UITextView 中的特定单词设置样式?
- python - 如何在不将整个图像加载到内存的情况下将单个 tif 图像部分加载到 numpy 数组中?
- javascript - 如何遍历链接对象树并将它们作为数组返回?
- google-apps-script - 无法将数组中的数据写入选定范围
- c# - 如何修改 C# 中特定路径的 IIS FTP 授权规则?
- excel - 如何复制和粘贴单元格,然后使用 excel VBA 以特定方式删除某些行?