PopBackStack 后片段弹出调用 OnViewCreated
Fragment popped call OnViewCreated after PopBackStack
我有 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_width="match_parent"
android:layout_height="match_parent"
android:tag="ListProductsFragment" />
片段 A 有一个转到片段 B 的按钮
片段 A -> 片段 B(使用 addToBackStack)
然后,我从片段B转到片段C
片段 B -> 片段 C(没有 addToBackStack)
我需要在片段 C 中保存项目时返回到片段 A,所以我不使用 addToBackStack。
问题是我在片段 C 中使用
requireActivity().supportFragmentManager.popBackStack()
或
requireActivity().onBackPressed()
出现了片段 A,但调用了片段 C 中的 OnViewCreated 方法,因此执行我在该片段 C 中的验证。
我需要从 Fragment C 返回到 Fragment A,而不调用 Fragment C 的 OnViewCreated
感兴趣的代码
主要Activity
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 (ScanFragment)
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()
}
}
})
}
好的,我想我明白了你的问题。您有条件地将内容添加到后台堆栈,这使片段管理器处于一种奇怪的状态。
问题
您从 Main 开始,将 Scanner 添加到返回堆栈,而不是 Product。因此,当您按回键时,您会将 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)
}
}
我有 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_width="match_parent"
android:layout_height="match_parent"
android:tag="ListProductsFragment" />
片段 A 有一个转到片段 B 的按钮
片段 A -> 片段 B(使用 addToBackStack)
然后,我从片段B转到片段C
片段 B -> 片段 C(没有 addToBackStack)
我需要在片段 C 中保存项目时返回到片段 A,所以我不使用 addToBackStack。
问题是我在片段 C 中使用
requireActivity().supportFragmentManager.popBackStack()
或
requireActivity().onBackPressed()
出现了片段 A,但调用了片段 C 中的 OnViewCreated 方法,因此执行我在该片段 C 中的验证。
我需要从 Fragment C 返回到 Fragment A,而不调用 Fragment C 的 OnViewCreated
感兴趣的代码
主要Activity
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 (ScanFragment)
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()
}
}
})
}
好的,我想我明白了你的问题。您有条件地将内容添加到后台堆栈,这使片段管理器处于一种奇怪的状态。
问题
您从 Main 开始,将 Scanner 添加到返回堆栈,而不是 Product。因此,当您按回键时,您会将 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)
}
}