Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

When you will resolve all problems with nested fragments in viewpager2?! #257

Closed
georrge1994 opened this issue Jun 7, 2023 · 1 comment

Comments

@georrge1994
Copy link

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")
    }
}
@georrge1994
Copy link
Author

georrge1994 commented Jun 8, 2023

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant