I am trying to achieve the following effect using FragmentTransaction.setCustomAnimations.
I have no problem getting the slide in animation setup. My problem is that I cannot figure out how to make Fragment A stay where it is and be UNDER Fragment B while the slide in animation is running. No matter what I do it seems that Fragment A is on top.
How can I achieve this?
Here is the FragmentTransaction code:
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.setCustomAnimations(R.anim.slide_in_right, R.anim.nothing, R.anim.nothing,
R.anim.slide_out_right);
ft.replace(R.id.fragment_content, fragment, name);
ft.addToBackStack(name);
ft.commit();
As you can see I have defined an animation R.anim.nothing for the "out" animation because I actually don't want Fragment A to do anything other than just stay where it is during the transaction.
Here are the animation resources:
slide_in_right.xml
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@android:integer/config_mediumAnimTime"
android:fromXDelta="100%p"
android:toXDelta="0"
android:zAdjustment="top" />
nothing.xml
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="@android:integer/config_mediumAnimTime"
android:fromAlpha="1.0"
android:toAlpha="1.0"
android:zAdjustment="bottom" />
Starting from fragment library 1.2.0
the recommanded way to fix this issue is to use FragmentContainerView
with FragmentTransaction.setCustomAnimations()
.
According to the documentation:
Fragments using exit animations are drawn before all others for FragmentContainerView. This ensures that exiting Fragments do not appear on top of the view.
Steps to fix this issue are:
androidx.fragment:fragment:1.2.0
;<fragment>
, <FrameLayout>
, or else) by <androidx.fragment.app.FragmentContainerView>
;FragmentTransaction.setCustomAnimations()
to animate your fragments transitions.Starting from Lollipop, you can increase de translationZ of your entering fragment. It will appear above the exiting one.
For example:
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ViewCompat.setTranslationZ(getView(), 100.f);
}
If you want to modify the translationZ value only for the duration of the animation, you should do something like this:
@Override
public Animation onCreateAnimation(int transit, final boolean enter, int nextAnim) {
Animation nextAnimation = AnimationUtils.loadAnimation(getContext(), nextAnim);
nextAnimation.setAnimationListener(new Animation.AnimationListener() {
private float mOldTranslationZ;
@Override
public void onAnimationStart(Animation animation) {
if (getView() != null && enter) {
mOldTranslationZ = ViewCompat.getTranslationZ(getView());
ViewCompat.setTranslationZ(getView(), 100.f);
}
}
@Override
public void onAnimationEnd(Animation animation) {
if (getView() != null && enter) {
ViewCompat.setTranslationZ(getView(), mOldTranslationZ);
}
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
return nextAnimation;
}
I don't know if you still need an answer but I recently needed to do the same and I found a way to do what you want.
I made something like this :
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
MyFragment next = getMyFragment();
ft.add(R.id.MyLayout,next);
ft.setCustomAnimations(R.anim.slide_in_right,0);
ft.show(next);
ft.commit();
I display my Fragment in a FrameLayout.
It work fines but the older Fragment is still in my View, I let android manage it like he wants because if I put:
ft.remove(myolderFrag);
it is not displayed during the animation.
slide_in_right.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >
<translate android:duration="150" android:fromXDelta="100%p"
android:interpolator="@android:anim/linear_interpolator"
android:toXDelta="0" />
</set>
I found a solution which works for me. I ended up using a ViewPager with a FragmentStatePagerAdapter. The ViewPager provides the swiping behavior and the FragmentStatePagerAdapter swaps in the fragments. The final trick to achieve the effect of having one page visible "under" the incoming page is to use a PageTransformer. The PageTransformer overrides the ViewPager's default transition between pages. Here is an example PageTransformer that achieves the effect with translation and a small amount of scaling on the left-hand side page.
public class ScalePageTransformer implements PageTransformer {
private static final float SCALE_FACTOR = 0.95f;
private final ViewPager mViewPager;
public ScalePageTransformer(ViewPager viewPager) {
this.mViewPager = viewPager;
}
@SuppressLint("NewApi")
@Override
public void transformPage(View page, float position) {
if (position <= 0) {
// apply zoom effect and offset translation only for pages to
// the left
final float transformValue = Math.abs(Math.abs(position) - 1) * (1.0f - SCALE_FACTOR) + SCALE_FACTOR;
int pageWidth = mViewPager.getWidth();
final float translateValue = position * -pageWidth;
page.setScaleX(transformValue);
page.setScaleY(transformValue);
if (translateValue > -pageWidth) {
page.setTranslationX(translateValue);
} else {
page.setTranslationX(0);
}
}
}
}
I found an alternate solution (not heavily tested) that I find more elegant than the proposals so far:
final IncomingFragment newFrag = new IncomingFragment();
newFrag.setEnterAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
clearSelection();
inFrag.clearEnterAnimationListener();
getFragmentManager().beginTransaction().remove(OutgoingFragment.this).commit();
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
getActivity().getSupportFragmentManager().beginTransaction()
.setCustomAnimations(R.anim.slide_in_from_right, 0)
.add(R.id.container, inFrag)
.addToBackStack(null)
.commit();
This is being called from within an inner class of the OutgoingFragment class.
A new fragment is being inserted, the animation completes, then the old fragment is being removed.
There may be some memory problems with this in some applications but it is better than retaining both fragments indefinitely.
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