I have problem making my fragments communicating with each other through the Activity
, which is using the FragmentPagerAdapter
, as a helper class that implements the management of tabs and all details of connecting a ViewPager
with associated TabHost
. I have implemented FragmentPagerAdapter
just as same as it is provided by the Android sample project Support4Demos.
The main question is how can I get particular fragment from FragmentManager
when I don't have neither Id or Tag? FragmentPagerAdapter
is creating the fragments and auto generating the Id and Tags.
the solution suggested by @personne3000 is nice, but it has one problem: when activity goes to the background and gets killed by the system (in order to get some free memory) and then restored, the
fragmentReferences
will be empty, becausegetItem
wouldn't be called.The class below handles such situation:
Summary of the problem
Note: In this answer I'm going to reference
FragmentPagerAdapter
and its source code. But the general solution should also apply toFragmentStatePagerAdapter
.If you're reading this you probably already know that
FragmentPagerAdapter
/FragmentStatePagerAdapter
is meant to createFragments
for yourViewPager
, but upon Activity recreation (whether from a device rotation or the system killing your App to regain memory) theseFragments
won't be created again, but instead their instances retrieved from theFragmentManager
. Now say yourActivity
needs to get a reference to theseFragments
to do work on them. You don't have anid
ortag
for these createdFragments
becauseFragmentPagerAdapter
set them internally. So the problem is how to get a reference to them without that information...Problem with current solutions: relying on internal code
A lot of the solutions I've seen on this and similar questions rely on getting a reference to the existing
Fragment
by callingFragmentManager.findFragmentByTag()
and mimicking the internally created tag:"android:switcher:" + viewId + ":" + id
. The problem with this is that you're relying on internal source code, which as we all know is not guaranteed to remain the same forever. The Android engineers at Google could easily decide to change thetag
structure which would break your code leaving you unable to find a reference to the existingFragments
.Alternate solution without relying on internal
tag
Here's a simple example of how to get a reference to the
Fragments
returned byFragmentPagerAdapter
that doesn't rely on the internaltags
set on theFragments
. The key is to overrideinstantiateItem()
and save references in there instead of ingetItem()
.or if you prefer to work with
tags
instead of class member variables/references to theFragments
you can also grab thetags
set byFragmentPagerAdapter
in the same manner: NOTE: this doesn't apply toFragmentStatePagerAdapter
since it doesn't settags
when creating itsFragments
.Note that this method does NOT rely on mimicking the internal
tag
set byFragmentPagerAdapter
and instead uses proper APIs for retrieving them. This way even if thetag
changes in future versions of theSupportLibrary
you'll still be safe.Don't forget that depending on the design of your
Activity
, theFragments
you're trying to work on may or may not exist yet, so you have to account for that by doingnull
checks before using your references.Also, if instead you're working with
FragmentStatePagerAdapter
, then you don't want to keep hard references to yourFragments
because you might have many of them and hard references would unnecessarily keep them in memory. Instead save theFragment
references inWeakReference
variables instead of standard ones. Like this:This class do the trick without relying on internal tags. Warning: Fragments should be accessed using the getFragment method and not the getItem one.
I managed to solve this issue by using ids instead of tags. (I am using I defined FragmentStatePagerAdapter which uses my custom Fragments in which I overrode the onAttach method, where you save the id somewhere:
And then you just access the fragment easily inside the activity:
you don't need to override
instantiateItem
nor rely on compatibility of creating fragment tags with internalmakeFragmentName
method.instantiateItem
is a public method so you can (and actually you should) call it inonCreate
method of your activity to get references to instances of your fragments and store them on local vars if you need. Just remember to surround a set ofinstantiateItem
calls withstartUpdate
andfinishUpdate
methods as described inPagerAdapter
javadoc:So for example this is the way to store references to your tab fragments in
onCreate
method:instantiateItem
will first try to get references to existing fragment instances fromFragmentManager
. Only if they don't exist yet, it will create new ones usinggetItem
method from your adapter and "store" them in theFragmentManager
for any future use.Some additional info:
If you don't call
instantiateItem
surrounded bystartUpdate
/finishUpdate
in youronCreate
method then you are risking that your fragment instances will never be committed toFragmentManager
: when your activity becomes foregroundinstantiateItem
will be called automatically to obtain your fragments, butstartUpdate
/finishUpdate
may not (depending on implementation details) and what they basically do is begin/commit aFragmentTransaction
.This may result in references to the created fragment instances being lost very quickly (for example when you rotate your screen) and recreated much more often than necessary. Depending on how "heavy" your fragments are, it may have a non-negligible performance consequences.
More importantly however, in such case instances of fragments stored on local vars will become stale: since android platform was not able to obtain the same instances from
FragmentManager
it may create and use new ones, while your vars will still be referencing the old ones.Not sure if my method was the correct or best way to do this since I am a relative beginner with Java/Android, but it did work (I'm sure it violates object oriented principles but no other solution worked for my use case).
I had a hosting Activity that was using a ViewPager with a FragmentStatePagerAdapter. In order to get references to the Fragments that were created by FragmentStatePagerAdapter I created a callback interface within the fragment class:
In the hosting activity I implemented the interface and created a LinkedHasSet to keep track of the fragments:
Within the ViewPagerFragment class I added the fragments to the list within onAttach and removed them within onDetach:
Within the hosting activity you'll now be able to use mFragments to iterate through the fragments that currently exist in the FragmentStatePagerAdapter.