I'm using a ViewPager
together with a FragmentStatePagerAdapter
to host three different fragments:
- [Fragment1]
- [Fragment2]
- [Fragment3]
When I want to get Fragment1
from the ViewPager
in the FragmentActivity
.
What is the problem, and how do I fix it?
The easiest and the most concise way. If all your fragments in
ViewPager
are of different classes you may retrieve and distinguish them as following:I implemented this easy with a bit different approach.
My custom FragmentAdapter.getItem method returned not new MyFragment(), but the instance of MyFragment that was created in FragmentAdapter constructor.
In my activity I then got the fragment from the adapter, check if it is instanceOf needed Fragment, then cast and use needed methods.
For grabbing fragments out of a ViewPager there are a lot of answers on here and on other related SO threads / blogs. Everyone I have seen is broken, and they generally seem to fall into one of the two types listed below. There are some other valid solutions if you only want to grab the current fragment, like this other answer on this thread.
If using
FragmentPagerAdapter
see below. If usingFragmentStatePagerAdapter
its worth looking at this. Grabbing indexes that are not the current one in a FragmentStateAdapter is not as useful as by the nature of it these will be completely torn down went out of view / out of offScreenLimit bounds.THE UNHAPPY PATHS
Wrong: Maintain your own internal list of fragments, added to when
FragmentPagerAdapter.getItem()
is calledSparseArray
orMap
getItem
is only called the first time a page is scrolled to (or obtained if yourViewPager.setOffscreenPageLimit(x)
> 0) in theViewPager
, if the hostingActivity
/Fragment
is killed or restarted then the internalSpaseArray
will be wiped out when the custom FragmentPagerActivity is recreated, but behind the scenes the ViewPagers internal fragments will be recreated, andgetItem
will NOT be called for any of the indexes, so the ability to get a fragment from index will be lost forever. You can account for this by saving out and restoring these fragment references viaFragmentManager.getFragment()
andputFragment
but this starts to get messy IMHO.Wrong: Construct your own tag id matching what is used under the hood in
FragmentPagerAdapter
and use this to retrieve the page Fragments from theFragmentManager
ViewPager
that could change at any time or for any OS version.The method thats recreated for this solution is
A HAPPY PATH:
ViewPager.instantiateItem()
A similar approach to
getItem()
above but non-lifecycle-breaking is to this is to hook intoinstantiateItem()
instead ofgetItem()
as the former will be called everytime that index is created / accessed. See this answerA HAPPY PATH: Construct your own
FragmentViewPager
Construct your own
FragmentViewPager
class from the source of the latest support lib and change the method used internally to generate the fragment tags. You can replace it with the below. This has the advantage that you know the tag creation will never change and your not relying on a private api / method, which is always dangerous.Then as the doc says, when you want to grab a fragment used for an index just call something like this method (which you can put in the custom
FragmentPagerAdapter
or a subclass) being aware the result may be null if getItem has not yet been called for that page i.e. its not been created yet.This is a simple solution and solves the issues in the other two solutions found everywhere on the web
Add next methods to your FragmentPagerAdapter:
getActiveFragment(0) has to work.
Here is the solution implemented into ViewPager https://gist.github.com/jacek-marchwicki/d6320ba9a910c514424d. If something fail you will see good crash log.
Easy way to iterate over fragments in fragment manager. Find viewpager, that has section position argument, placed in public static PlaceholderFragment newInstance(int sectionNumber).
I handled it by first making a list of all the fragments (
List<Fragment> fragments;
) that I was going to use then added them to the pager making it easier to handle the currently viewed fragment.So:
so then this can be called:
so then i could chuck it in an if statement that would only run if it was on the correct fragment
but thats just my hack and slash approach but it worked for me, I use it do do relevent changes to my currently displayed fragment when the back button is pushed.