When you will resolve all problems with nested fragments in viewpager2?!
georrge1994 opened this issue · comments
Too muach bugs, hacks and problems with viewpager2! There is my perfect solution with nested fragments in an another fragment, but I still have huge problems with stability (the fcking detaching adapter from GC). No memory leaks! But nested fragments are alive and go throw fragment-life-circle. Every time when I leave from screen and return back (restarted both fragments) I got one additional instance of NoteListFragment! Previous viewPager1 was not optimized, but it works! You have to retire the person who created this sht!
NotesFragment - Class with viewpager2:
internal class NotesFragment : SearchToolbarFragment<NotesViewModel>(NotesViewModel::class) {
private val viewBinding by viewBinding(FragmentNotesBinding::bind)
private lateinit var zoomOutPageTransformer: ZoomOutPageTransformer
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
zoomOutPageTransformer = ZoomOutPageTransformer()
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? =
inflater.inflate(R.layout.fragment_notes, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewBinding.viewPager2.adapter = NotesViewPagerAdapter(childFragmentManager, viewLifecycleOwner.lifecycle)
viewBinding.viewPager2.setPageTransformer(zoomOutPageTransformer)
viewBinding.viewPager2.isSaveEnabled = false
TabLayoutMediator(viewBinding.tabLayout, viewBinding.viewPager2) { tab, position ->
tab.text = when (position) {
0 -> context?.getString(R.string.notes_fragment_tab_title_by_lessons)
1 -> context?.getString(R.string.notes_fragment_tab_title_own_notes)
else -> context?.getString(R.string.notes_fragment_tab_title_own_notes)
}
}.attach()
}
override fun onDestroyView() {
viewBinding.viewPager2.adapter = null
super.onDestroyView()
}
}
NotesViewPagerAdapter:
internal class NotesViewPagerAdapter(
fragmentManager: FragmentManager,
viewLifecycle: Lifecycle
) : FragmentStateAdapter(fragmentManager, viewLifecycle) {
override fun getItemCount() = NUM_PAGES
override fun createFragment(position: Int): Fragment = when (position) {
0 -> NoteListFragment.newInstance(NotesTabTypes.BY_LESSONS)
1 -> NoteListFragment.newInstance(NotesTabTypes.OWN_NOTES)
else -> NoteListFragment.newInstance(NotesTabTypes.OWN_NOTES)
}
}
NoteListFragment is inner fragment.
internal class NoteListFragment : NavigationFragment() {
private val viewBinding by viewBinding(FragmentNoteListBinding::bind)
private lateinit var adapter: NotesRecyclerViewAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
adapter = NotesRecyclerViewAdapter(requireContext(), noteActions).also { it.setHasStableIds(true) }
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? =
inflater.inflate(R.layout.fragment_note_list, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewBinding.recyclerView.adapter = adapter
}
override fun onDestroyView() {
adapter.detachRecyclerView()
viewBinding.recyclerView.adapter = null
super.onDestroyView()
println("NoteListFragment onDestroyView")
}
}
I have resolved my problem with manually removing the old fragments from fragmentManager. I don't reuse old detached adapters, so I don't to have old instances of nested fragments in fragment manager.
override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) {
super.onDetachedFromRecyclerView(recyclerView)
fragmentManager.beginTransaction().apply {
fragmentManager.fragments.forEach { fragment ->
if (fragment is NoteListFragment) {
detach(fragment)
}
}
}.commitAllowingStateLoss()
}
UPDATE: Also I had to add the same code for onAttachedToRecyclerView
before super call. Without it I still had a leak by screen rotation.