4

i used this ViewPager2 dependency:

implementation 'androidx.viewpager2:viewpager2:1.0.0'

then i wrote code and make Adapter for FragmentStateAdapter like below:

private inner class ViewPagerAdapter(fragmentManager: FragmentManager,
                                         lifecycle: Lifecycle, config: TribunConfig?)
        : FragmentStateAdapter(fragmentManager, lifecycle) {
        private val config: TribunConfig?
        override fun createFragment(position: Int): Fragment {
            return TribunNewsFragment.newInstance(if (menuTribun!!.get(position) == "News") "home"
            else menuTribun!!.get(position)!!.replace(" ", "").toLowerCase(), config)!!
        }

        override fun getItemCount(): Int {
            return menuTribun!!.size
        }

        init {
            this.config = config
        }
    }

an in method onCreate i implement some code related viewpager2 and its adapter like below:

        viewPager2!!.offscreenPageLimit = 1
        viewPager2!!.adapter = ViewPagerAdapter(supportFragmentManager, lifecycle, config)
        TabLayoutMediator(tabLayout!!, viewPager2!!, TabLayoutMediator.TabConfigurationStrategy { tab: TabLayout.Tab?, position: Int -> tab!!.text = menuTribun!!.get(position) }).attach()

But i have problem when i want to make swipeRefresh. My swipeRefresh needs current fragment to access the method inside fragment. But there is NO METHOD getItem in FragmentStateAdapter. Anyone can help me?

Nanda Z
  • 1,604
  • 4
  • 15
  • 37

2 Answers2

3
  1. Create property in your adapter to save all your fragments
  2. Save current position
  3. Get fragment from fragment list (property) by position when you need it

Something like this:

    val fragments: MutableList<Fragment> = mutableListOf()

    private val config: TribunConfig?
    override fun createFragment(position: Int): Fragment {
        val fragment = TribunNewsFragment.newInstance(if (menuTribun!!.get(position) == "News") "home"
        else menuTribun!!.get(position)!!.replace(" ", "").toLowerCase(), config)!!

        fragments.add(fragment)
        return fragment
    }

Parent:

 yourTabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
        override fun onTabSelected(tab: TabLayout.Tab?) {
            val fragment = adapter.fragments[tab!!.position]
            // cast fragment to your fragment class and do what you want
        }

        override fun onTabUnselected(tab: TabLayout.Tab?) {
        }

        override fun onTabReselected(tab: TabLayout.Tab?) {
        }
    })

Also you can use when or if-else for your fragments position or create base class with methods you need and cast getting fragment to it.

Oleg Skidan
  • 617
  • 6
  • 24
  • 1
    As a note ViewPager2 has a page changed callback https://developer.android.com/reference/androidx/viewpager2/widget/ViewPager2.OnPageChangeCallback so this could be done without Tabs – Andrew Apr 08 '20 at 09:40
  • But, i had counter problem. When i try to click tab which the index is more than size of mutableList and it cause crash – Nanda Z Apr 08 '20 at 10:00
  • How is it possible? Do you have more tabs than fragments? – Oleg Skidan Apr 08 '20 at 10:02
  • 1
    it's because of `viewPager2!!.offscreenPageLimit = 1`, so i don't create all fragment just two fragment for first opening – Nanda Z Apr 08 '20 at 13:03
  • So it's easy. CurrentTab.position+1 is maximum for you, except lastPosition. – Oleg Skidan Apr 08 '20 at 13:09
  • I think i can accept your answer, because i add delay time for event in `onTabSelected`. It's works now – Nanda Z Apr 08 '20 at 13:20
  • 8
    This doesn't handle configuration changes (i.e. app running out of memory in the background) - `createFragment` is not called when that happens, it is restored without calling that and your list becomes empty. – lawonga May 13 '20 at 23:36
  • @Iawonga is right. You need to store the fragments in a `FragmentManager` and retrieve them after the config change using `putFragment` and `getFragment` respectively. This is obviously a lot of boilerplate and I think the more formal answer is to avoid relying on fragment references in your activity if possible. I've yet to see a good example by google on how to handle this. – rosghub Aug 29 '20 at 03:17
  • An alternative would be [this answer](https://stackoverflow.com/a/61178226/4322363) that relies on the internal logic of `ViewPager2` storing fragments by tag. – rosghub Aug 29 '20 at 03:28
  • This causes memory leak issues for the fragments maintained in mutablelist. – Sagar Pujari Apr 12 '22 at 12:06
1

Following @Oleg's answer, you can access the Fragments like this in your Adapter instead of creating a local list and adding each fragment on createFragment method:

private val fragmentList: List<TribunNewsFragment>
        get() {
            return fragment.childFragmentManager.fragments.filterIsInstance<TribunNewsFragment>()
        }

It's important to use childFragmentManager instead of fragmentManager if you are creating the ViewPager inside another fragment.

And it survives configuration changes, regarding @lawonga's comment on the answer's topic.

  • 1
    It looks like you already have a `fragment` reference here. How are you referencing it from your adapter? – rosghub Aug 22 '20 at 22:01