blob: f6f32c3ae8758a51b2beea58869c575cf6e44a76 [file] [log] [blame]
// Copyright 2013 Google Inc. All Rights Reserved.
package com.android.deskclock.widget.sgv;
import android.R.interpolator;
import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.Point;
import android.view.View;
import android.view.WindowManager;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import java.util.List;
public class SgvAnimationHelper {
/**
* Supported entrance animations for views in the {@link StaggeredGridView}.
*/
public enum AnimationIn {
NONE,
// Fly in all views from the bottom of the screen
FLY_UP_ALL_VIEWS,
// New views expand into view from height 0. Existing views are updated and translated
// to their new positions if appropriate.
EXPAND_NEW_VIEWS,
// New views expand into view from height 0. Existing views are updated and translated
// to their new positions if appropriate. This animation is done for all views
// simultaneously without a cascade effect.
EXPAND_NEW_VIEWS_NO_CASCADE,
// New views are flown in from the bottom. Existing views are updated and translated
// to their new positions if appropriate.
FLY_IN_NEW_VIEWS,
// New views are slid in from the side. Existing views are updated and translated
// to their new positions if appropriate.
SLIDE_IN_NEW_VIEWS,
// Fade in all new views
FADE,
}
/**
* Supported exit animations for views in the {@link StaggeredGridView}.
*/
public enum AnimationOut {
NONE,
// Stale views are faded out of view. Existing views are then updated and translated
// to their new positions if appropriate.
FADE,
// Stale views are dropped to the bottom of the screen. Existing views are then updated
// and translated to their new positions if appropriate.
FLY_DOWN,
// Stale views are slid to the side of the screen. Existing views are then updated
// and translated to their new positions if appropriate.
SLIDE,
// Stale views are collapsed to height 0. Existing views are then updated
// and translated to their new positions if appropriate.
COLLAPSE
}
private static Interpolator sDecelerateQuintInterpolator;
private static final int ANIMATION_LONG_SCREEN_SIZE = 1600;
private static final int ANIMATION_MED_SCREEN_SIZE = 1200;
// Duration of an individual animation when the children of the grid are laid out again. These
// are measured in milliseconds and based on the height of the screen.
private static final int ANIMATION_SHORT_DURATION = 400;
private static final int ANIMATION_MED_DURATION = 450;
private static final int ANIMATION_LONG_DURATION = 500;
public static final float ANIMATION_ROTATION_DEGREES = 25.0f;
/**
* Duration of an individual animation when the children of the grid are laid out again.
* This is measured in milliseconds.
*/
private static int sAnimationDuration = ANIMATION_MED_DURATION;
public static void initialize(Context context) {
sDecelerateQuintInterpolator = AnimationUtils.loadInterpolator(context,
interpolator.decelerate_quint);
final Point size = new Point();
((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).
getDefaultDisplay().getSize(size);
final int screenHeight = size.y;
if (screenHeight >= ANIMATION_LONG_SCREEN_SIZE) {
sAnimationDuration = ANIMATION_LONG_DURATION;
} else if (screenHeight >= ANIMATION_MED_SCREEN_SIZE) {
sAnimationDuration = ANIMATION_MED_DURATION;
} else {
sAnimationDuration = ANIMATION_SHORT_DURATION;
}
}
public static int getDefaultAnimationDuration() {
return sAnimationDuration;
}
public static Interpolator getDefaultAnimationInterpolator() {
return sDecelerateQuintInterpolator;
}
/**
* Add animations to translate a view's X-translation. {@link AnimatorListener} can be null.
*/
private static void addXTranslationAnimators(List<Animator> animators,
final View view, int startTranslation, final int endTranslation, int animationDelay,
AnimatorListener listener) {
// We used to skip the animation if startTranslation == endTranslation,
// but to add a recycle view listener, we need at least one animation
view.setTranslationX(startTranslation);
final ObjectAnimator translateAnimatorX = ObjectAnimator.ofFloat(view,
View.TRANSLATION_X, startTranslation, endTranslation);
translateAnimatorX.setInterpolator(sDecelerateQuintInterpolator);
translateAnimatorX.setDuration(sAnimationDuration);
translateAnimatorX.setStartDelay(animationDelay);
if (listener != null) {
translateAnimatorX.addListener(listener);
}
animators.add(translateAnimatorX);
}
/**
* Add animations to translate a view's Y-translation. {@link AnimatorListener} can be null.
*/
private static void addYTranslationAnimators(List<Animator> animators,
final View view, int startTranslation, final int endTranslation, int animationDelay,
AnimatorListener listener) {
// We used to skip the animation if startTranslation == endTranslation,
// but to add a recycle view listener, we need at least one animation
view.setTranslationY(startTranslation);
final ObjectAnimator translateAnimatorY = ObjectAnimator.ofFloat(view,
View.TRANSLATION_Y, startTranslation, endTranslation);
translateAnimatorY.setInterpolator(sDecelerateQuintInterpolator);
translateAnimatorY.setDuration(sAnimationDuration);
translateAnimatorY.setStartDelay(animationDelay);
if (listener != null) {
translateAnimatorY.addListener(listener);
}
animators.add(translateAnimatorY);
}
/**
* Translate a view to the specified translation values, and animate the translations to 0.
*/
public static void addXYTranslationAnimators(List<Animator> animators, final View view,
int xTranslation, int yTranslation, int animationDelay) {
addXTranslationAnimators(animators, view, xTranslation, 0, animationDelay, null);
addYTranslationAnimators(animators, view, yTranslation, 0, animationDelay, null);
}
/**
* Translate a view to the specified translation values, while rotating to the specified
* rotation value.
*/
public static void addTranslationRotationAnimators(List<Animator> animators, final View view,
int xTranslation, int yTranslation, float rotation, int animationDelay) {
addXYTranslationAnimators(animators, view, xTranslation, yTranslation, animationDelay);
view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
view.setRotation(rotation);
final ObjectAnimator rotateAnimatorY = ObjectAnimator.ofFloat(view,
View.ROTATION, view.getRotation(), 0.0f);
rotateAnimatorY.setInterpolator(sDecelerateQuintInterpolator);
rotateAnimatorY.setDuration(sAnimationDuration);
rotateAnimatorY.setStartDelay(animationDelay);
rotateAnimatorY.addListener(new AnimatorListenerAdapter() {
private boolean mIsCanceled = false;
@Override
public void onAnimationCancel(Animator animation) {
mIsCanceled = true;
}
@Override
public void onAnimationEnd(Animator animation) {
if (!mIsCanceled) {
view.setRotation(0);
}
view.setLayerType(View.LAYER_TYPE_NONE, null);
}
});
animators.add(rotateAnimatorY);
}
/**
* Expand a view into view by scaling up vertically from 0.
*/
public static void addExpandInAnimators(List<Animator> animators, final View view,
int animationDelay) {
view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
view.setScaleY(0);
final ObjectAnimator scaleAnimatorY = ObjectAnimator.ofFloat(view,
View.SCALE_Y, view.getScaleY(), 1.0f);
scaleAnimatorY.setInterpolator(sDecelerateQuintInterpolator);
scaleAnimatorY.setDuration(sAnimationDuration);
scaleAnimatorY.setStartDelay(animationDelay);
scaleAnimatorY.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
view.setScaleY(1.0f);
view.setLayerType(View.LAYER_TYPE_NONE, null);
}
});
animators.add(scaleAnimatorY);
}
/**
* Collapse a view out by scaling it from its current scaled value to 0.
*/
public static void addCollapseOutAnimators(List<Animator> animators, final View view,
int animationDelay) {
view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
final ObjectAnimator scaleAnimatorY = ObjectAnimator.ofFloat(view, View.SCALE_Y,
view.getScaleY(), 0);
scaleAnimatorY.setInterpolator(sDecelerateQuintInterpolator);
scaleAnimatorY.setDuration(sAnimationDuration);
scaleAnimatorY.setStartDelay(animationDelay);
scaleAnimatorY.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
view.setScaleY(0);
view.setLayerType(View.LAYER_TYPE_NONE, null);
}
});
animators.add(scaleAnimatorY);
}
/**
* Collapse a view out by scaling it from its current scaled value to 0.
* The animators are expected to run immediately without a start delay.
*/
public static void addCollapseOutAnimators(List<Animator> animators, final View view) {
addCollapseOutAnimators(animators, view, 0 /* animation delay */);
}
/**
* Fly a view out by moving it vertically off the bottom of the screen.
*/
public static void addFlyOutAnimators(List<Animator> animators,
final View view, int startTranslation, int endTranslation, int animationDelay) {
addYTranslationAnimators(animators, view, startTranslation, endTranslation,
animationDelay, null);
}
public static void addFlyOutAnimators(List<Animator> animators, final View view,
int startTranslation, int endTranslation) {
addFlyOutAnimators(animators, view, startTranslation,
endTranslation, 0 /* animation delay */);
}
public static void addSlideInFromRightAnimators(List<Animator> animators, final View view,
int startTranslation, int animationDelay) {
addXTranslationAnimators(animators, view, startTranslation, 0, animationDelay, null);
addFadeAnimators(animators, view, 0, 1.0f, animationDelay);
}
/**
* Slide a view out of view from the start to end position, fading the view out as it
* approaches the end position.
*/
public static void addSlideOutAnimators(List<Animator> animators, final View view,
int startTranslation, int endTranslation, int animationDelay) {
addFadeAnimators(animators, view, view.getAlpha(), 0, animationDelay);
addXTranslationAnimators(animators, view, startTranslation, endTranslation,
animationDelay, null);
}
/**
* Slide a view out of view from the start to end position, fading the view out as it
* approaches the end position. The animators are expected to run immediately without a
* start delay.
*/
public static void addSlideOutAnimators(List<Animator> animators, final View view,
int startTranslation, int endTranslation) {
addSlideOutAnimators(animators, view, startTranslation,
endTranslation, 0 /* animation delay */);
}
/**
* Add animations to fade a view from the specified start alpha value to end value.
*/
public static void addFadeAnimators(List<Animator> animators, final View view,
float startAlpha, final float endAlpha, int animationDelay) {
if (startAlpha == endAlpha) {
return;
}
view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
view.setAlpha(startAlpha);
final ObjectAnimator fadeAnimator = ObjectAnimator.ofFloat(view, View.ALPHA,
view.getAlpha(), endAlpha);
fadeAnimator.setInterpolator(sDecelerateQuintInterpolator);
fadeAnimator.setDuration(sAnimationDuration);
fadeAnimator.setStartDelay(animationDelay);
fadeAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
view.setAlpha(endAlpha);
view.setLayerType(View.LAYER_TYPE_NONE, null);
}
});
animators.add(fadeAnimator);
}
/**
* Add animations to fade a view from the specified start alpha value to end value.
* The animators are expected to run immediately without a start delay.
*/
public static void addFadeAnimators(List<Animator> animators, final View view,
float startAlpha, final float endAlpha) {
addFadeAnimators(animators, view, startAlpha, endAlpha, 0 /* animation delay */);
}
}