This is my current workaround for anybody interested.
In the function for adding the new Fragment
:
final Fragment toRemove = fragmentManager.findFragmentById(containerID);
if (toRemove != null) {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
fragmentManager.beginTransaction().hide(toRemove).commit();
}
},
getResources().getInteger(android.R.integer.config_mediumAnimTime) + 100);
// Use whatever duration you chose for your animation for this handler
// I added an extra 100 ms because the first transaction wasn't always
// fast enough
}
fragmentManager.beginTransaction()
.setCustomAnimations(enter, 0, 0, popExit).add(containerID, fragmentToAdd)
.addToBackStack(tag).commit();
and in onCreate
:
final FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.addOnBackStackChangedListener(
new FragmentManager.OnBackStackChangedListener() {
@Override
public void onBackStackChanged() {
Fragment current = fragmentManager.findFragmentById(containerID);
if (current != null && current.isHidden()) {
fragmentManager.beginTransaction().show(current).commit();
}
}
});
I would prefer some sort of AnimationListener instead of a Handler above, but I didn't see any way you could use one to detect the end of the transaction animation that wasn't tied to the fragment like onCreateAnimation()
. Any suggestions/edits with an appropriate listener would be appreciated.
I will point out the Fragment
s I am adding this way are lightweight so it isn't a problem for me to have them in the fragment container along with the fragment they are on top of.
If you want to remove the fragment you could put fragmentManager.beginTransaction().remove(toRemove).commitAllowingStateLoss();
in the Handler
's Runnable
, and in the OnBackStackChangedListener
:
// Use back stack entry tag to get the fragment
Fragment current = getCurrentFragment();
if (current != null && !current.isAdded()) {
fragmentManager.beginTransaction()
.add(containerId, current, current.getTag())
.commitNowAllowingStateLoss();
}
Note the above solution doesn't work for the first fragment in the container (because it isn't in the back stack) so you would have to have another way to restore that one, perhaps save a reference to the first fragment somehow... But if you don't use the back stack and always replace fragments manually, this is not an issue. OR you could add all fragments to the back stack (including the first one) and override onBackPressed
to make sure your activity exits instead of showing a blank screen when only one fragment is left in the back stack.
EDIT:
I discovered the following functions that could probably replace FragmentTransaction.remove()
and FragmentTransaction.add()
above:
FragmentTransaction
.detach():
Detach the given fragment from the UI. This is the same state as when it is put on the back stack: the fragment is removed from the UI, however its state is still being actively managed by the fragment manager. When going into this state its view hierarchy is destroyed.
FragmentTransaction
.attach():
Re-attach a fragment after it had previously been detached from the UI with detach(Fragment). This causes its view hierarchy to be re-created, attached to the UI, and displayed.