Andrew Kirmse
Andrew Kirmse

Reputation: 85

Determining when fragment no longer visible in ViewPager2

I am looking to migrate from ViewPager to ViewPager2. My existing ViewPager contains ListFragments that support bulk editing via an ActionMode. I need to cancel the current bulk editing operation when the user swipes from one page to another, so that the context header from one page isn't shown when viewing a different page.

With ViewPager, I can override setPrimaryItem in the ListFragment's FragmentPagerAdapter to know when one page replaces another. Crucially, this function takes as a parameter the actual child fragment that's becoming visible, which I can store in the adapter and later use to cancel bulk editing when it is replaced by another fragment.

How do I do the same thing with ViewPager2, namely, know when a fragment is no longer visible? I have tried overriding Fragment.onPause, but that's not what I want---it gets called whenever the phone screen turns off or the orientation changes, and I don't want to cancel bulk editing in these circumstances. I also have a callback registered via ViewPager2.registerOnPageChangeCallback, but this gives me only the index of the new fragment, not the fragment itself or anything about the old fragment.

Upvotes: 4

Views: 1080

Answers (1)

Cliabhach
Cliabhach

Reputation: 1550

I think the best solution is to register a FragmentTransactionCallback. Downside - this was only added in the latest 1.1.0 alpha release of the ViewPager2 library.

There's a method on that class (FragmentTransactionCallback::onFragmentMaxLifecyclePreUpdated) which is called whenever the ViewPager2 changes a Fragment's maximum Lifecycle state.

val basicAdapter: FragmentStateAdapter = // ....

basicAdapter.registerFragmentTransactionCallback(object : FragmentTransactionCallback() {
    override fun onFragmentMaxLifecyclePreUpdated(fragment: Fragment, max: Lifecycle.State): OnPostEventListener {
        // Check whether a Fragment is going to move from 'resumed' to 'started'
        //
        // Note that this check won't catch some types of smooth scroll:
        // I'm not certain why, but I think it has to do with fragments
        // already being paused before the method is called.
        //
        if (max == Lifecycle.State.STARTED && fragment.isResumed) {
            // This fragment WAS the 'current item' but it's offscreen now.
            return OnPostEventListener { TODO("Cancel Bulk Editing") }
        }
        return super.onFragmentMaxLifecyclePreUpdated(fragment, max)
    }
})

Upvotes: 3

Related Questions