Reputation: 806
The setup is like so:
Single activity app (MainActivity) with nav_graph
HomeFragment has viewPager2 with 3 subfragments
Each of these 3 fragments are same fragment with RecyclerView showing different list of posts
User can click on a post and that navigates to completely new screen/fragment (Details)
Clicking "Back" button throws exception:
java.lang.IllegalStateException: FragmentManager is already executing transactions at androidx.fragment.app.FragmentManager.ensureExecReady(FragmentManager.java:1790) at androidx.fragment.app.FragmentManager.execSingleAction(FragmentManager.java:1826) at androidx.fragment.app.BackStackRecord.commitNow(BackStackRecord.java:297) at androidx.viewpager2.adapter.FragmentStateAdapter$FragmentMaxLifecycleEnforcer.updateFragme
So my feeling is that issue happens between ViewPager2 with RecyclerView and FragmentTransitions through navigation. Why? Well, clicking "back" arrow makes the transaction to 'home' screen, but also home screen having viewpager also deals with fragment transitions and this somehow creates the issue.
To give more context, this is how tabs/viewpager is set (in HomeFragment):
val viewPager: ViewPager2 = container.findViewById(R.id.viewPager)
viewPager.adapter = PagerAdapter(parentFragmentManager, lifecycle)
val tabLayout: TabLayout = container.findViewById(R.id.tabLayout)
val names = arrayOf("Najbolje", "Popularno", "Novo")
TabLayoutMediator(tabLayout, viewPager) { tab, position -> tab.text = names[position] }.attach()
Here's the adapter:
class PagerAdapter(fragmentManager: FragmentManager, lifecycle: Lifecycle) : FragmentStateAdapter(fragmentManager, lifecycle) {
private val fragments = listOf<Fragment>(
MyListFragment.newInstance(Type.TOP),
MyListFragment.newInstance(Type.POPULAR),
MyListFragment.newInstance(Type.NEW)
)
override fun getItemCount(): Int {
return fragments.size
}
override fun createFragment(position: Int): Fragment {
return fragments[position]
}
}
and here's the code for setting up recyclerview:
private fun setupRecyclerView() {
val adapter = MyListAdapter(requireContext(), myService)
binding.recyclerView.adapter = adapter
binding.recyclerView.layoutManager = layoutManager
viewModel.posts.observe(viewLifecycleOwner) { post -> adapter.submitList(posts.map { PostItem(it) }) }
addScrollListener()
}
One of the similar issues was somewhere solved by changing parentFragmentManager to childFragmentManager within setTabs method, but that creates another issue that throws exception among the lines of 'RecyclerView already has layoutManager set up'
Let me know if not enough information has been provided.
Upvotes: 2
Views: 1289
Reputation: 199880
You're using the wrong FragmentManager
- for every fragment that is fully contained within the layout of another (such as your ViewPager2
managed fragments), you must use childFragmentManager
to properly nest the fragments.
viewPager.adapter = PagerAdapter(childFragmentManager, lifecycle)
This is required not only to restore your state properly (something which using the activity's supportFragmentManager
will not do), but to ensure that the parent fragment goes through its state transitions first and only then does the child fragments go through their transitions, fixing the "already executing transactions" issue.
Upvotes: 3
Reputation: 806
It appears by trying out whole lot of different similar implementation so far I wasn't able to reproduce the issue after changing the "parentFragmentManager" into: activity?.supportFragmentManager
Upvotes: 0