Android get current fragments

How I can retrieve current fragment in NavHostFragment?

I tried to find a method in the new Navigation components but I didn’t find anything about that.

I have the current destination with :

But I can’t get any reference to the displayed fragment.

10 Answers 10

Navigation does not provide any mechanism for getting the implementation (i.e., the Fragment itself) of the current destination.

As per the Creating event callbacks to the activity, you should either communicate with your Fragment by

  • Having the Fragment register a callback in its onAttach method, casting your Activity to an instance of an interface you provide
  • Use a shared ViewModel that your Activity and Fragment use to communicate.

Reference to the displayed fragment (AndroidX):

Here nav_host_fragment is an ID of the fragment tag in your activity_main.xml with android:name=»androidx.navigation.fragment.NavHostFragment»

You can do something like this:

But for more advanced communication Listeners with callback methods registered in Fragment.onAttach() ( Fragment -> Activity rather one direction communication ) and SharedViewModel ( bidirectional , important to have ViewModelProviders, and Lifecycle owner that is scoped to getActivity() rather)

Based on other answers

Using Michal’s answer I wrote this extension function for testing:

@Override public void onBackPressed() < super.onBackPressed();

From an Activity which has NavHostFragment, below code snippet can be used to retrieve the instance of the Active Fragment.

val currentFragment = mNavHostFragment?.childFragmentManager?.primaryNavigationFragment

I post my complete answer with androidx. Care : in my case i needed to retrieve one of the child fragment (could not be the first).

In MainActivity you should have something like :

Then you have to create a method to retrieve the good fragment.

Finally you get it.

Really the best and correct way to handle this is to use an interface. View models should really be used for passing data between the activity and fragments. Here is how I solved this problem:

Make sure activity implements interface

Make sure to create lateinit var for each fragment you need to reference and then

Now in each fragment in the onCreateView method, make sure to create the interface listener and passback the fragment with the interface

Now back in the activity, you should be able to instantiate your fragment objects for reference via interface callback

Источник

Get current fragment with ViewPager2

I’m migrating my ViewPager to ViewPager2 since the latter is supposed to solve all the problems of the former. Unfortunately, when using it with a FragmentStateAdapter , I don’t find any way to get the currently displayed fragment.

viewPager.getCurrentItem() gives the current displayed index and adapter.getItem(index) generally creates a new Fragment for the current index. Unless keeping a reference to all created fragments in getItem() , I have no idea how to access the currently displayed fragment.

With the old ViewPager , one solution was to call adapter.instantiateItem(index) which would return the fragment at the desired index.

Am I missing something with ViewPager2 ?

14 Answers 14

In ViewPager2 the FragmentManager by default have assigned tags to fragments like this:

Fragment in 1st position has a tag of «f0»

Fragment in 2nd position has a tag of «f1»

Fragment in 3rd position has a tag of «f2» and so on. so you can get your fragment’s tag and by concatenating the «f» with position of your fragment. To get the current Fragment you can get current position from the viewPager2 position and make your tag like this (For Kotlin):

For fragment at a certain position

You can cast the Fragment and always check if it is not null if you are using this technique.

Читайте также:  Cognex barcode scanner android

If you host your ViewPager2 in Fragment, use childFragmentManager instead.

REMEMBER

If you have overriden the getItemId(position: Int) in your adapter. Then your case is different. It should be:

If you host your ViewPager2 in Fragment, use childFragmentManager instead of supportFragmentManager .

I had similar problem when migrating to ViewPager2 .

In my case I decided to use parentFragment property (I think you can make it also work for activity) and hope, that ViewPager2 will keep only the current fragment resumed. (i.e. page fragment that was resumed last is the current one.)

So in my main fragment ( HostFragment ) that contains ViewPager2 view I created following property:

I decided to use WeakReference , so I don’t leak inactive Fragment instances

And each of my fragments that I display inside ViewPager2 inherits from common super class MyPageFragment . This class is responsible for registering its instance with host fragment in onResume :

I also used this class to define common interface of paged fragments:

And then I can call them from the HostFragment like this:

I’m not saying it’s a nice solution, but I think it’s more elegant than relying on internals of ViewPager’s adapter with instantiateItem method that we had to use before.

The solution to find current Fragment by its tag seems the most suitable for me. I’ve created these extension functions for that:

  • If your ViewPager2 host is Activity , use supportFragmentManager or fragmentManager .
  • If your ViewPager2 host is Fragment , use childFragmentManager
  • findFragmentAtPosition will work only for Fragments that were initialized in ViewPager2’s RecyclerView. Therefore you can get only the positions that are visible + 1.
  • Lint will suggest you to remove ViewPager2. from fun ViewPager2.findFragmentAtPosition , because you don’t use anything from ViewPager2 class. I think it should stay there, because this workaround applies solely to ViewPager2.

I was able to get access to current fragment in FragmentStateAdapter using reflection.

Extension function in Kotlin:

Add Kotlin reflection dependency if needed:

with FragmentStateAdapter in placeFragmentInViewHolder(@NonNull final FragmentViewHolder holder) add Fragment

If you want the current Fragment to only perform some action in it, you can use a SharedViewModel which is shared between ViewPager container and its Fragment s and pass an Identifier to each fragment and observe to a LiveData in SharedViewModel . Set value of that LiveData to an object which consists of Identifier of the fragment you want to update (i.e. Pair which String is type of the Identifier ). Then inside your observers check if the current emitted Identifer is as same as the fragment’s Identifier or not and consume data if it is equal.
Its not as simple as using fragment’s tags to find them, But it least you do not need to worry about changes to how ViewPager2 create tag for each fragment.

May as well post my solution to this — it’s the same basic approach as @Almighty ‘s, except I’m keeping the Fragment weak references in a lookup table in the PagerAdapter :

and then you can call getFragment with the appropriate page number, like adapter.currentPage or whatever.

So basically, the adapter is keeping its own cache of fragments it’s created, but with WeakReference s so it’s not actually holding onto them, once the components actually using the fragments are done with them, they won’t be in the cache anymore. So you can hold a lookup for all the current fragments.

You could have the getter just return the (nullable) result of the lookup, if you wanted. This version obviously creates the fragment if it doesn’t already exist, which is useful if you expect it to be there. This can be handy if you’re using ViewPager2.OnPageChangeCallback , which will fire with the new page number before the view pager creates the fragment — you can «get» the page, which will create and cache it, and when the pager calls createFragment it should still be in the cache and avoid recreating it.

Читайте также:  Прозрачная кнопка андроид студио

It’s not guaranteed the weak reference won’t have been garbage collected between those two moments though, so if you’re setting stuff on that fragment instance (rather than just reading something from it, like a title you want to display) be aware of that!

Similar to some other answers here, this is what I’m currently using.
I’m not clear on how reliable it is but at least one of these is bound to work 😁.

fm is the supportFragmentManager

The ViewPagerAdapter is intended to hide all these implementation details which is why there is no straight-forward way to do it.

You could try setting and id or tag on the fragment when you instantiate it in getItem() then use fragmentManager.findFragmentById() or fragmentManager.findFragmentByTag() to retrieve.

Doing it like this, however, is a bit of a code smell. It suggests to me that stuff is being done in the activity when it should be done in the fragment (or elsewhere).

Perhaps there is another approach to achieve what you want but it’s hard to give suggestions without knowing why you need to get the current fragment.

I had the same problem. I converted from ViewPager to ViewPager2, using FragmentStateAdapter. In my case I have a DetailActivity class (extends AppCompatActivity) which houses the ViewPager2, which is used to page through lists of data (Contacts, Media, etc.) on smaller form-factor devices.

I need to know the currently shown fragment (which is my own class DetailFragment which extends androidx.fragment.app.Fragment), because that class contains the string I use to update the title on the DetailActivity toolbar.

I first started down the road of registering an onPageChangeCallback listener as suggested by some, but I quickly ran into problems:

  1. I initially created tags during the ViewPager2’s adapter.createFragment() call as suggested by some with the idea to add the newly created fragment to a Bundle object (using FragmentManager.put() ) with that tag. This way I could then save them across config changes. The problem here is that during createFragment() , the fragment isn’t actually yet part of the FragmentManager, so the put() calls fail.
  2. The other problem is that if the fundamental idea is to use the onPageSelected() method of the OnPageChangeCallback to find the fragment using the internally generated «f»+position tag names — we again have the same timing issue: The very first time in, onPageSelected() is called PRIOR to the createFragment() call on the adapter — so there are no fragments yet created and added to the FragmentManager, so I can’t get a reference to that first fragment using the «f0» tag.
  3. I then tried to find a way where I could save the position passed in to onPageSelected, then try and reference that somewhere else in order to retrieve the fragment after the adapter had made the createFragment() calls — but I could not identify any type of handler within the adapter, the associated recyclerview, the viewpager etc. that allows me to surface the list of fragments that I could then reference that to the position identified within that listener. Strangely, for example, one adapter method that looked very promising was onViewAttachedToWindow() — however it is marked final so can’t be overridden (even though the JavaDoc clearly anticipates it being used this way).

So what I ended up doing that worked for me was the following:

  1. In my DetailFragment class, I created an interface that can be implemented by the hosting activity:
  1. Then I added code within onResume() of DetailFragment to see if the associated activity has implemented the DetailFragmentShownListener interface within this class, and if so I make the callback:
  1. Then where I need to know when this fragment is shown (such as within DetailActivity), I implement the interface and when it receives this callback I know that this is the current fragment:
Читайте также:  Teclast p80 планшет экран 8 дюймов android 1280x800 2 гб озу 32 гб пзу

mCurrentFragment is a property of this class as its used in various other places.

Источник

Getting the current Fragment instance in the viewpager

Below is my code which has 3 Fragment classes each embedded with each of the 3 tabs on ViewPager . I have a menu option. As shown in the onOptionsItemSelected() , by selecting an option, I need to update the fragment that is currently visible. To update that I have to call a method which is in the fragment class. Can someone please suggest how to call that method?

Suppose below is the fragment class with the method updateList() I want to call:

31 Answers 31

by selecting an option, I need to update the fragment that is currently visible.

A simple way of doing this is using a trick related to the FragmentPagerAdapter implementation:

Please rethink your variable naming convention, using as the variable name the name of the class is very confusing(so no ViewPager ViewPager , use ViewPager mPager for example).

First of all keep track of all the «active» fragment pages. In this case, you keep track of the fragment pages in the FragmentStatePagerAdapter, which is used by the ViewPager.

To avoid keeping a reference to «inactive» fragment pages, you need to implement the FragmentStatePagerAdapter’s destroyItem(. ) method:

and when you need to access the currently visible page, you then call:

Where the MyAdapter’s getFragment(int) method looks like this:

Hope it may help!

This is the only way I don’t get NullPointerException for the instance variables of that particular fragment classes. This might be helpful for others who stuck at the same thing. In the onOptionsItemSelected(), I coded the below way:

FragmentStatePagerAdapter has public method with the name instantiateItem that return your fragment based on specified parameter values, this method has two parameters ViewGroup (ViewPager) and position.

Used this method to get specified position’s fragment,

I know its too late but I have really simple ways of doing it,

// for fragment at 0 possition

Cast the instance retreived from above line to the fragment you want to work on with. Works perfectly fine.

is the pager instance managing the fragments.

There are a lot of answers here that don’t really address the basic fact that there’s really NO WAY to do this predictably, and in a way that doesn’t result you shooting yourself in the foot at some point in the future.

FragmentStatePagerAdapter is the only class that knows how to reliably access the fragments that are tracked by the FragmentManager — any attempt to try and guess the fragment’s id or tag is not reliable, long-term. And attempts to track the instances manually will likely not work well when state is saved/restored, because FragmentStatePagerAdapter may well not call the callbacks when it restores the state.

About the only thing that I’ve been able to make work is copying the code for FragmentStatePagerAdapter and adding a method that returns the fragment, given a position ( mFragments.get(pos) ). Note that this method assumes that the fragment is actually available (i.e. it was visible at some point).

If you’re particularly adventurous, you can use reflection to access the elements of the private mFragments list, but then we’re back to square one (the name of the list is not guaranteed to stay the same).

Источник

Оцените статью