package org.wikipedia.views; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.content.res.Resources; import android.support.annotation.Nullable; import android.view.View; /** * Contains convenient methods for performing various animations on Views. */ public final class ViewAnimations { private static long SHORT_ANIMATION_DURATION; private static long MEDIUM_ANIMATION_DURATION; private ViewAnimations() { } public static void init(Resources resources) { SHORT_ANIMATION_DURATION = resources.getInteger(android.R.integer.config_shortAnimTime); MEDIUM_ANIMATION_DURATION = resources.getInteger(android.R.integer.config_mediumAnimTime); } /** * Crossfades two views, one of which is assumed to be currently visible * @param curView The view that is currently visible * @param newView The new view that should be faded in */ public static void crossFade(View curView, View newView) { fadeIn(newView); fadeOut(curView); } /** * Crossfades two views, one of which is assumed to be currently visible * @param curView The view that is currently visible * @param newView The new view that should be faded in * @param runOnComplete Optional Runnable to be run when the animation is complete (may be null). */ public static void crossFade(View curView, View newView, Runnable runOnComplete) { fadeIn(newView); fadeOut(curView, runOnComplete); } /** * Fades in a view. * @param view The currently invisible view to be faded in */ public static void fadeIn(View view) { fadeIn(view, null); } /** * Fades in a view. * @param view The currently invisible view to be faded in * @param runOnComplete Optional Runnable to be run when the animation is complete (may be null). */ public static void fadeIn(View view, @Nullable final Runnable runOnComplete) { view.setAlpha(0); view.setVisibility(View.VISIBLE); view.animate() .alpha(1) .setDuration(MEDIUM_ANIMATION_DURATION) .setListener(new CancelStateAnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { if (!canceled()) { if (runOnComplete != null) { runOnComplete.run(); } } } }) .start(); } /** * Fades out a view. * @param view The currently visible view to be faded out */ public static void fadeOut(final View view) { fadeOut(view, null); } /** * Fades out a view. * @param view The currently visible view to be faded out * @param runOnComplete Optional Runnable to be run when the animation is complete (may be null). */ public static void fadeOut(final View view, final Runnable runOnComplete) { view.animate().cancel(); view.animate() .alpha(0) .setDuration(MEDIUM_ANIMATION_DURATION) .setListener(new CancelStateAnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { if (!canceled()) { // Detect if we got canceled, and if so DON'T hide... // There's another animation now pushing the alpha back up view.setVisibility(View.GONE); view.setAlpha(1); if (runOnComplete != null) { runOnComplete.run(); } } } }) .start(); } /** * Ensures that the translationY of a particular view is the given value. * * If it isn't the current value, then it performs a short animation to make it so. * * @param view The view to translate * @param translation The value to ensure it is translated by */ public static void ensureTranslationY(View view, int translation) { if (view.getTranslationY() != translation) { view.animate().translationY(translation).setDuration(SHORT_ANIMATION_DURATION).start(); } } private static class CancelStateAnimatorListenerAdapter extends AnimatorListenerAdapter { private boolean canceled; @Override public void onAnimationCancel(Animator animation) { canceled = true; } protected boolean canceled() { return canceled; } } }