在 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

popBackStack导致一次又一次调用片段的oncreateView

在popBackStack之后,ViewPager中的碎片不是显示器

在 Popbackstack() 上调用方法

FragmentManager popBackStack 不删除片段