I've encountered a really hard to diagnose issue in an Android app. getUserVisibleHint() returns false on the currently selected fragment in a ViewPager when it should return true (because it is visible and selected).
I've characterized the instances I see this behavior as follows:
ViewPager
FragmentStatePagerAdapter
PagerAdapter
Debugging revealed that FragmentStatePagerAdapter is actually setting the state of the selected tab properly in setPrimaryItem(ViewGroup container, int position, Object object) but that it is later set to false in FragmentManager#moveToState(Fragment f, int newState, int transit, int transitionStyle, boolean keepActive)
//from FragmentManager#moveToState(Fragment f, int newState, int transit, int transitionStyle, boolean keepActive)
f.mUserVisibleHint = f.mSavedFragmentState.getBoolean(
FragmentManagerImpl.USER_VISIBLE_HINT_TAG, true);
f.mSavedFragmentState above has saved the visible state as false because it was saved when the fragment was no longer on the screen.
So the issue here is state loss; the visible state is being set in FragmentStatePagerAdapter#setPrimaryItem but is lost some time before the fragment's onResume method is called.
Until this bug is fixed in the library, override setPrimaryItem in your PagerAdapter and force any pending transactions to commit first.
public static class SectionsPagerAdapter extends FragmentStatePagerAdapter {
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
//Force any pending transactions to save before we set an item as primary
finishUpdate(null);
super.setPrimaryItem(container, position, object);
}
@Override
public Fragment getItem(int position) {
Fragment fragment = new DummySectionFragment();
Bundle args = new Bundle();
args.putInt(DummySectionFragment.ARG_SECTION_NUMBER, position + 1);
fragment.setArguments(args);
return fragment;
}
@Override
public int getCount() {
return 4;
}
@Override
public CharSequence getPageTitle(int position) {
return "Page " + (position + 1);
}
}
To fix this, FragmentStatePagerAdapter must commit any fragment transactions before setting the user visible hint.
FragmentStatePagerAdapterJust to show what's happening inside FragmentStatePagerAdapter
@Override
public Object instantiateItem(ViewGroup container, int position) {
// If we already have this item instantiated, there is nothing
// to do. This can happen when we are restoring the entire pager
// from its saved state, where the fragment manager has already
// taken care of restoring the fragments we previously had instantiated.
if (mFragments.size() > position) {
Fragment f = mFragments.get(position);
if (f != null) {
return f;
}
}
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
Fragment fragment = getItem(position);
if (DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment);
if (mSavedState.size() > position) {
Fragment.SavedState fss = mSavedState.get(position);
if (fss != null) {
fragment.setInitialSavedState(fss);
}
}
while (mFragments.size() <= position) {
mFragments.add(null);
}
fragment.setMenuVisibility(false);
fragment.setUserVisibleHint(false);
mFragments.set(position, fragment);
mCurTransaction.add(container.getId(), fragment);
return fragment;
}
@Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
Fragment fragment = (Fragment)object;
if (fragment != mCurrentPrimaryItem) {
if (mCurrentPrimaryItem != null) {
mCurrentPrimaryItem.setMenuVisibility(false);
mCurrentPrimaryItem.setUserVisibleHint(false);
}
if (fragment != null) {
fragment.setMenuVisibility(true);
fragment.setUserVisibleHint(true);
}
mCurrentPrimaryItem = fragment;
}
}
@Override
public void finishUpdate(ViewGroup container) {
if (mCurTransaction != null) {
mCurTransaction.commitNowAllowingStateLoss();
mCurTransaction = null;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With