I am trying to achieve the following effect using FragmentTransaction.setCustomAnimations.
Based on jfrite answer attaching my implementations
import android.content.res.Resources;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.view.ViewCompat;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.util.Log;
public final class AnimationHelper {
private AnimationHelper() {
}
private static String TAG = AnimationHelper.class.getSimpleName();
private static final float ELEVATION_WHILE_ENTER_ANIMATION_IS_RUNNING = 100f;
private static final int RESTORE_ANIMATION_DELAY = 16;
/**
* When replacing fragments with animations, by default the new fragment is placed below the replaced fragment. This
* method returns an animation object that sets high elevation at the beginning of the animation and resets the
* elevation when the animation completes. The {@link Animation} object that is returned is not the actual object
* that is used for the animating the fragment but the callbacks are called at the appropriate time. The method
* {@link Fragment#onCreateAnimation(int, boolean, int)} by default returns null, therefor, this method can be used
* as the return value for {@link Fragment#onCreateAnimation(int, boolean, int)} method although it can return
* null.
* @param enter True if fragment is 'entering'.
* @param nextAnim Animation resource id that is about to play.
* @param fragment The animated fragment.
* @return If nextAnim is a valid resource id and 'enter' is true, returns an {@link Animation} object with the
* described above behavior, otherwise returns null.
*/
@Nullable
public static Animation increaseElevationWhileAnimating(boolean enter, int nextAnim,
@NonNull Fragment fragment) {
if (!enter || nextAnim == 0) {
return null;
}
Animation nextAnimation;
try {
nextAnimation = AnimationUtils.loadAnimation(fragment.getContext(), nextAnim);
} catch (Resources.NotFoundException e) {
Log.e(TAG, "Can't find animation resource", e);
return null;
}
nextAnimation.setAnimationListener(new Animation.AnimationListener() {
private float oldTranslationZ;
@Override
public void onAnimationStart(Animation animation) {
if (fragment.getView() != null && !fragment.isDetached()) {
oldTranslationZ = ViewCompat.getTranslationZ(fragment.getView());
ViewCompat.setTranslationZ(fragment.getView(), ELEVATION_WHILE_ENTER_ANIMATION_IS_RUNNING);
}
}
@Override
public void onAnimationEnd(Animation animation) {
if (fragment.getView() != null && !fragment.isDetached()) {
fragment.getView().postDelayed(() -> {
// Decreasing the elevation at the ned can cause some flickering because of timing issues,
// Meaning that the replaced fragment did not complete yet the animation. Resting the animation
// with a minor delay solves the problem.
if (!fragment.isDetached()) {
ViewCompat.setTranslationZ(fragment.getView(), oldTranslationZ);
}
}, RESTORE_ANIMATION_DELAY);
}
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
return nextAnimation;
}
}
Here is how I use the helper form the fragment.
@Override
public Animation onCreateAnimation(int transit, final boolean enter, int nextAnim) {
return AnimationHelper.increaseElevationWhileAnimating(enter, nextAnim, this);
}
Here is how i start the fragment with animation
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.setCustomAnimations(R.anim.slide_in, R.anim.hold, R.anim.hold, R.anim.slide_out);