在 PopBackStack 之后片段弹出调用 OnViewCreated
Posted
技术标签:
【中文标题】在 PopBackStack 之后片段弹出调用 OnViewCreated【英文标题】:Fragment popped call OnViewCreated after PopBackStack 【发布时间】:2021-06-30 18:23:10 【问题描述】:我有 1 个 Activity 和 3 个片段。 (A、B 和 C)。所以,
Activity -> 带有片段 A 的 FragmentContainerView
<androidx.fragment.app.FragmentContainerView
android:id="@+id/host_fragment"
android:name="cl.gersard.shoppingtracking.ui.product.list.ListProductsFragment"
android:layout_
android:layout_
android:tag="ListProductsFragment" />
Fragment A 有一个按钮可以转到 Fragment B
片段 A -> 片段 B(带有 addToBackStack)
然后,我从 Fragment B 转到 Fragment C
片段 B -> 片段 C(没有 addToBackStack)
当我在 Fragment C 中保存一个项目时,我需要回到 Fragment A,所以我不使用 addToBackStack。
问题是我在 Fragment C 中使用时
requireActivity().supportFragmentManager.popBackStack()
或
requireActivity().onBackPressed()
片段 A 出现,但片段 C 中的 OnViewCreated 方法被调用,因此执行我在该片段 C 中的验证。
我需要从 Fragment C 回到 Fragment A 而不调用 Fragment C 的 OnViewCreated
感兴趣的代码
主活动
fun changeFragment(fragment: Fragment, addToBackStack: Boolean)
val transaction = supportFragmentManager.beginTransaction()
.replace(R.id.host_fragment, fragment,fragment::class.java.simpleName)
if (addToBackStack) transaction.addToBackStack(null)
transaction.commit()
片段 A (ListProductsFragment)
override fun onViewCreated(view: View, savedInstanceState: Bundle?)
super.onViewCreated(view, savedInstanceState)
setupRecyclerView()
observeLoading()
observeProducts()
viewModel.fetchProducts()
viewBinding.btnEmptyProducts.setOnClickListener viewModel.fetchProducts()
viewBinding.fabAddPurchase.setOnClickListener addPurchase()
private fun addPurchase()
(requireActivity() as MainActivity).changeFragment(ScanFragment.newInstance(),true)
片段 B(扫描片段)
override fun barcodeDetected(barcode: String)
if (processingBarcode.compareAndSet(false, true))
(requireActivity() as MainActivity).changeFragment(PurchaseFragment.newInstance(barcode), false)
片段 C (PurchaseFragment)
private fun observePurchaseState()
viewModel.purchasesSaveState.observe(viewLifecycleOwner, purchaseState ->
when (purchaseState)
is PurchaseSaveState.Error -> TODO()
is PurchaseSaveState.Loading -> manageProgress(purchaseState.isLoading)
PurchaseSaveState.Success ->
Toast.makeText(requireActivity(), getString(R.string.purchase_saved_successfully), Toast.LENGTH_SHORT).show()
requireActivity().supportFragmentManager.popBackStack()
)
完整代码在这里https://github.com/gersard/PurchaseTracking
【问题讨论】:
onViewCreated
应该只在片段是......好吧,正在创建时调用。如果发生这种情况,您可能会无意中再次打开 C 的某些逻辑错误。检查您的代码或发布一个完整的工作示例来说明您的问题。
@dominicoder 我编辑了一段代码和回购链接,谢谢
我无聊到下载你的项目并运行它。我无法重现您所描述的问题。
@dominicoder 重现问题扫描 X 条码,在 Fragment C 中会出现 un Snackbar 并显示“未找到产品...”的消息,然后按返回按钮,再次扫描条码,snackbar 再次出现,然后按返回键,snackbar 将出现两次(在 Fragment A,产品列表中)
【参考方案1】:
好的,我想我看到了你的问题。您正在有条件地向后台堆栈添加东西,这会使片段管理器处于奇怪的状态。
问题
您从 Main 开始,将 Scanner 添加到后端堆栈,而不是 Product。因此,当您按下返回键时,您会将扫描仪从堆栈中弹出,但产品仍保留在 FragmentManager 中。这就是为什么每次扫描并返回时都会获取一个新实例的原因。 为什么我不清楚发生这种情况 - 似乎可能是 Android 错误?您正在替换片段,所以奇怪的是额外的实例正在建立。
一个解决方案
将您的 changeFragment
实现更改为有条件地弹出堆栈,而不是有条件地向其中添加内容。
fun changeFragment(fragment: Fragment, popStack: Boolean)
if (keepStack) supportFragmentManager.popBackStack()
val transaction = supportFragmentManager.beginTransaction()
.replace(R.id.host_fragment, fragment,fragment::class.java.simpleName)
transaction.addToBackStack(null) // Always add the new fragment so "back" works
transaction.commit()
然后反转你当前调用changeFragment
的逻辑:
private fun addPurchase()
// Pass false to not pop the main activity
(requireActivity() as MainActivity)
.changeFragment(ScanFragment.newInstance(), false)
还有……
override fun barcodeDetected(barcode: String)
if (processingBarcode.compareAndSet(false, true))
// Pass true to pop the barcode that's currently there
(requireActivity() as MainActivity)
.changeFragment(PurchaseFragment.newInstance(barcode), true)
【讨论】:
以上是关于在 PopBackStack 之后片段弹出调用 OnViewCreated的主要内容,如果未能解决你的问题,请参考以下文章
popBackStack导致一次又一次调用片段的oncreateView