4

ViewPager release 1.0.0 version

For a normal RecyclerView, holder.itemView gives me the view currently binding/ rendering.

However, FragmentStateAdapter's holder.itemView only gives me a FrameLayout

My ViewPager adapater:

class MyFragmentStateAdapter(activity: FragmentActivity) :
    FragmentStateAdapter(activity) {
    var items = mutableListOf<String>()
    override fun createFragment(position: Int): MyPageFragment {
        val itemText = items[position]
        return MyPageFragment.create(itemText)
    }

    override fun getItemCount(): Int = items.size

    override fun onBindViewHolder(
        holder: FragmentViewHolder,
        position: Int,
        payloads: MutableList<Any>
    ) {
        super.onBindViewHolder(holder, position, payloads)
        val fragment = ??? as MyRefreshableInterface
        fragment.refresh()
    }

    fun update(mutableListOf: MutableList<String>) {
        this.items = mutableListOf
        notifyDataSetChanged()
    }
}

Context

I have a small and fix number of tabs displaying different information section of a user profile. Upon certain events, I need to refresh AUTOMATICALLY all the tabs ASAP. In other words, I need to refresh the currently-hidden tabs besides the current tab user is looking at.

ASAP = the next time user visits a currently-hidden tab. User goes there, first thing they see is a loading animation. That's good

Why not immediately?

Because hidden fragments could be detached/ destroyed. User is not looking at the right now anyway. Keeping all the fragments in the memory is also expensive

When user navigates to previous hidden tab say Addresses, onBindViewHolder will be triggered (This is great compared to ViewPager 1) However, the gap is that I have no reference of the currently-selected fragment

Other findings

I've already try referencing fragmentActivity.supportFragmentManager.fragments but it seems to have maximum of 4 fragments whereas I have 6 fragments/ pages, for the sake of testing

ericn
  • 12,476
  • 16
  • 84
  • 127

1 Answers1

0

You seem to be asking 2 different things.

I need to refresh automatically all the tabs. In other words, I need to refresh the currently-hidden tabs besides the current tab user is looking at.

and

When user navigates to previous hidden tab say Addresses, onBindViewHolder will be triggered.

You are trying to do the update when displayed on this second item.


This "Update when displayed" is easy with viewpager2, when a Fragment is displayed the Fragment is brought up to lifecycle state "Resumed" from "Started".

Thus create an update method in each Fragment and then in the Fragments onResume method call the update method.

(This is what I do in my App)

Update:
The docs on this behaviour is bad in viewpager2, the best I can find is the comment in the source code https://android.googlesource.com/platform/frameworks/support/+/refs/heads/androidx-master-dev/viewpager2/viewpager2/src/main/java/androidx/viewpager2/adapter/FragmentStateAdapter.java#634 of

/** * Pauses (STARTED) all Fragments that are attached and not a primary item. * Keeps primary item Fragment RESUMED. */

Or the release notes https://developer.android.com/jetpack/androidx/releases/viewpager2#1.0.0-alpha06

FragmentStateAdapter: non-primary-item Fragments are capped at STARTED, and their menuVisibility is set to false.

Viewpager 1 in Androidx has been updated with BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT to match the behaviour of Viewpager2 See https://developer.android.com/reference/androidx/fragment/app/FragmentStatePagerAdapter#BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT


The "Update all Fragments" is bit harder but I believe you can do the following (sorry my Kotlin is not up to scratch to give code example.

In the Adapter Constructor create and store a List of the Fragments (Not just itemText)
e.g. foreach item in items add to list of Fragments MyPageFragment.create(itemText)

Then in createFragment just return the Fragment from the correct position of the list.
e.g. return fragments[position]

Then in your adapter update you iterate over all items in your List of Fragements calling update on each Fragment.

(This is basically maintaining your own list of Fragments in the adapter instead of creating them on the fly when createFragment is called)

Update:
As noted in the comments this is less than ideal and breaks the Fragment lifecycle concepts and efficiency of Viewpager2, the update in onResume of Fragment is better.

Andrew
  • 8,198
  • 2
  • 15
  • 35
  • In ViewPager 1, `onResume` is often not call. Can you point me to the doc where it says `onResume` will be called in ViewPager2? *In the Adapter Constructor create and store a List of the Fragments* is expensive and fragile – ericn Apr 13 '20 at 15:56
  • So the docs are bad on this but if you look at the release notes https://developer.android.com/jetpack/androidx/releases/viewpager2#1.0.0-alpha06 "FragmentStateAdapter: non-primary-item Fragments are capped at STARTED, and their menuVisibility is set to false." Also viewpager1 has been updated to match this behaviour and is better documented See https://developer.android.com/reference/androidx/fragment/app/FragmentStatePagerAdapter#FragmentStatePagerAdapter(androidx.fragment.app.FragmentManager,%20int) with `BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT` – Andrew Apr 13 '20 at 17:12
  • Viewpager1 used to bring all fragments up to `Resumed` but in Androidx that has been deprecated as it breaks the lifecycle concept and `BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT` is the only option as it matches viewpager2's behaviour. – Andrew Apr 13 '20 at 17:16