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
I was able to get access to current fragment in FragmentStateAdapter using reflection.
Extension function in Kotlin:
fun FragmentStateAdapter.getItem(position: Int): Fragment? {
return this::class.superclasses.find { it == FragmentStateAdapter::class }
?.java?.getDeclaredField("mFragments")
?.let { field ->
field.isAccessible = true
val mFragments = field.get(this) as LongSparseArray<Fragment>
return@let mFragments[getItemId(position)]
}
}
Add Kotlin reflection dependency if needed:
implementation "org.jetbrains.kotlin:kotlin-reflect:1.3.61"
Example call:
val tabsAdapter = viewpager.adapter as FragmentStateAdapter
val currentFragment = tabsAdapter.getItem(viewpager.currentItem)
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
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:
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.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.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:
public interface DetailFragmentShownListener {
// Allows classes that extend this to update visual items after shown
void onDetailFragmentShown(DetailFragment me);
}
public void onResume() {
super.onResume();
View v = getView();
if (v!=null && v.getViewTreeObserver().isAlive()) {
v.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
v.getViewTreeObserver().removeOnGlobalLayoutListener(this);
// Let our parent know we are laid out
if ( getActivity() instanceof DetailFragmentShownListener ) {
((DetailFragmentShownListener) getActivity()).onDetailFragmentShown(DetailFragment.this);
}
}
});
}
}
@Override
public void onDetailFragmentShown(DetailFragment me) {
mCurrentFragment = me;
updateToolbarTitle();
}
mCurrentFragment is a property of this class as its used in various other places.
I also had your problem and used this trick
Map<Integer, Fragment> map = new HashMap<>();
@Override
public Fragment createFragment(int position) {
Fragment fragment;
switch (position) {
case 0:
fragment = new CircularFragment();
map.put(0, fragment);
return fragment;
case 1:
fragment = new LocalFragment();
map.put(1, fragment);
return fragment;
case 2:
fragment = new SettingsFragment();
map.put(2, fragment);
return fragment;
}
fragment = new CircularFragment();
map.put(0, fragment);
return fragment;
}
and you can get fragment like this
LocalFragment f = (LocalFragment) map.get(1);
supportFragmentManager.findFragmentByTag("f" + viewpager.currentItem)
with FragmentStateAdapter in placeFragmentInViewHolder(@NonNull final FragmentViewHolder holder)add Fragment
mFragmentManager.beginTransaction()
.add(fragment, "f" + holder.getItemId())
.setMaxLifecycle(fragment, STARTED)
.commitNow()
The solution to find current Fragment by its tag seems the most suitable for me. I've created these extension functions for that:
fun ViewPager2.findCurrentFragment(fragmentManager: FragmentManager): Fragment? {
return fragmentManager.findFragmentByTag("f$currentItem")
}
fun ViewPager2.findFragmentAtPosition(
fragmentManager: FragmentManager,
position: Int
): Fragment? {
return fragmentManager.findFragmentByTag("f$position")
}
Activity, use supportFragmentManager
or fragmentManager.Fragment, use childFragmentManagerNote that:
findFragmentAtPosition will work only for Fragments that were initialized in ViewPager2's RecyclerView. Therefore you can get only the positions that are visible + 1.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.