1 // Copyright 2013 Google Inc. All Rights Reserved. 2 3 package com.android.deskclock.widget.sgv; 4 5 import android.R.interpolator; 6 import android.animation.Animator; 7 import android.animation.Animator.AnimatorListener; 8 import android.animation.AnimatorListenerAdapter; 9 import android.animation.ObjectAnimator; 10 import android.content.Context; 11 import android.graphics.Point; 12 import android.view.View; 13 import android.view.WindowManager; 14 import android.view.animation.AnimationUtils; 15 import android.view.animation.Interpolator; 16 17 18 import java.util.List; 19 20 public class SgvAnimationHelper { 21 22 /** 23 * Supported entrance animations for views in the {@link StaggeredGridView}. 24 */ 25 public enum AnimationIn { 26 NONE, 27 // Fly in all views from the bottom of the screen 28 FLY_UP_ALL_VIEWS, 29 // New views expand into view from height 0. Existing views are updated and translated 30 // to their new positions if appropriate. 31 EXPAND_NEW_VIEWS, 32 // New views expand into view from height 0. Existing views are updated and translated 33 // to their new positions if appropriate. This animation is done for all views 34 // simultaneously without a cascade effect. 35 EXPAND_NEW_VIEWS_NO_CASCADE, 36 // New views are flown in from the bottom. Existing views are updated and translated 37 // to their new positions if appropriate. 38 FLY_IN_NEW_VIEWS, 39 // New views are slid in from the side. Existing views are updated and translated 40 // to their new positions if appropriate. 41 SLIDE_IN_NEW_VIEWS, 42 // Fade in all new views 43 FADE, 44 } 45 46 /** 47 * Supported exit animations for views in the {@link StaggeredGridView}. 48 */ 49 public enum AnimationOut { 50 NONE, 51 // Stale views are faded out of view. Existing views are then updated and translated 52 // to their new positions if appropriate. 53 FADE, 54 // Stale views are dropped to the bottom of the screen. Existing views are then updated 55 // and translated to their new positions if appropriate. 56 FLY_DOWN, 57 // Stale views are slid to the side of the screen. Existing views are then updated 58 // and translated to their new positions if appropriate. 59 SLIDE, 60 // Stale views are collapsed to height 0. Existing views are then updated 61 // and translated to their new positions if appropriate. 62 COLLAPSE 63 } 64 65 private static Interpolator sDecelerateQuintInterpolator; 66 67 private static final int ANIMATION_LONG_SCREEN_SIZE = 1600; 68 private static final int ANIMATION_MED_SCREEN_SIZE = 1200; 69 70 // Duration of an individual animation when the children of the grid are laid out again. These 71 // are measured in milliseconds and based on the height of the screen. 72 private static final int ANIMATION_SHORT_DURATION = 400; 73 private static final int ANIMATION_MED_DURATION = 450; 74 private static final int ANIMATION_LONG_DURATION = 500; 75 76 public static final float ANIMATION_ROTATION_DEGREES = 25.0f; 77 78 /** 79 * Duration of an individual animation when the children of the grid are laid out again. 80 * This is measured in milliseconds. 81 */ 82 private static int sAnimationDuration = ANIMATION_MED_DURATION; 83 84 public static void initialize(Context context) { 85 sDecelerateQuintInterpolator = AnimationUtils.loadInterpolator(context, 86 interpolator.decelerate_quint); 87 88 final Point size = new Point(); 89 ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)). 90 getDefaultDisplay().getSize(size); 91 final int screenHeight = size.y; 92 93 if (screenHeight >= ANIMATION_LONG_SCREEN_SIZE) { 94 sAnimationDuration = ANIMATION_LONG_DURATION; 95 } else if (screenHeight >= ANIMATION_MED_SCREEN_SIZE) { 96 sAnimationDuration = ANIMATION_MED_DURATION; 97 } else { 98 sAnimationDuration = ANIMATION_SHORT_DURATION; 99 } 100 } 101 102 public static int getDefaultAnimationDuration() { 103 return sAnimationDuration; 104 } 105 106 public static Interpolator getDefaultAnimationInterpolator() { 107 return sDecelerateQuintInterpolator; 108 } 109 110 /** 111 * Add animations to translate a view's X-translation. {@link AnimatorListener} can be null. 112 */ 113 private static void addXTranslationAnimators(List<Animator> animators, 114 final View view, int startTranslation, final int endTranslation, int animationDelay, 115 AnimatorListener listener) { 116 // We used to skip the animation if startTranslation == endTranslation, 117 // but to add a recycle view listener, we need at least one animation 118 view.setTranslationX(startTranslation); 119 final ObjectAnimator translateAnimatorX = ObjectAnimator.ofFloat(view, 120 View.TRANSLATION_X, startTranslation, endTranslation); 121 translateAnimatorX.setInterpolator(sDecelerateQuintInterpolator); 122 translateAnimatorX.setDuration(sAnimationDuration); 123 translateAnimatorX.setStartDelay(animationDelay); 124 if (listener != null) { 125 translateAnimatorX.addListener(listener); 126 } 127 128 animators.add(translateAnimatorX); 129 } 130 131 /** 132 * Add animations to translate a view's Y-translation. {@link AnimatorListener} can be null. 133 */ 134 private static void addYTranslationAnimators(List<Animator> animators, 135 final View view, int startTranslation, final int endTranslation, int animationDelay, 136 AnimatorListener listener) { 137 // We used to skip the animation if startTranslation == endTranslation, 138 // but to add a recycle view listener, we need at least one animation 139 view.setTranslationY(startTranslation); 140 final ObjectAnimator translateAnimatorY = ObjectAnimator.ofFloat(view, 141 View.TRANSLATION_Y, startTranslation, endTranslation); 142 translateAnimatorY.setInterpolator(sDecelerateQuintInterpolator); 143 translateAnimatorY.setDuration(sAnimationDuration); 144 translateAnimatorY.setStartDelay(animationDelay); 145 146 if (listener != null) { 147 translateAnimatorY.addListener(listener); 148 } 149 150 animators.add(translateAnimatorY); 151 } 152 153 /** 154 * Translate a view to the specified translation values, and animate the translations to 0. 155 */ 156 public static void addXYTranslationAnimators(List<Animator> animators, final View view, 157 int xTranslation, int yTranslation, int animationDelay) { 158 addXTranslationAnimators(animators, view, xTranslation, 0, animationDelay, null); 159 addYTranslationAnimators(animators, view, yTranslation, 0, animationDelay, null); 160 } 161 162 /** 163 * Translate a view to the specified translation values, while rotating to the specified 164 * rotation value. 165 */ 166 public static void addTranslationRotationAnimators(List<Animator> animators, final View view, 167 int xTranslation, int yTranslation, float rotation, int animationDelay) { 168 addXYTranslationAnimators(animators, view, xTranslation, yTranslation, animationDelay); 169 170 view.setLayerType(View.LAYER_TYPE_HARDWARE, null); 171 view.setRotation(rotation); 172 173 final ObjectAnimator rotateAnimatorY = ObjectAnimator.ofFloat(view, 174 View.ROTATION, view.getRotation(), 0.0f); 175 rotateAnimatorY.setInterpolator(sDecelerateQuintInterpolator); 176 rotateAnimatorY.setDuration(sAnimationDuration); 177 rotateAnimatorY.setStartDelay(animationDelay); 178 rotateAnimatorY.addListener(new AnimatorListenerAdapter() { 179 private boolean mIsCanceled = false; 180 181 @Override 182 public void onAnimationCancel(Animator animation) { 183 mIsCanceled = true; 184 } 185 186 @Override 187 public void onAnimationEnd(Animator animation) { 188 if (!mIsCanceled) { 189 view.setRotation(0); 190 } 191 192 view.setLayerType(View.LAYER_TYPE_NONE, null); 193 } 194 }); 195 196 animators.add(rotateAnimatorY); 197 } 198 199 /** 200 * Expand a view into view by scaling up vertically from 0. 201 */ 202 public static void addExpandInAnimators(List<Animator> animators, final View view, 203 int animationDelay) { 204 view.setLayerType(View.LAYER_TYPE_HARDWARE, null); 205 view.setScaleY(0); 206 207 final ObjectAnimator scaleAnimatorY = ObjectAnimator.ofFloat(view, 208 View.SCALE_Y, view.getScaleY(), 1.0f); 209 scaleAnimatorY.setInterpolator(sDecelerateQuintInterpolator); 210 scaleAnimatorY.setDuration(sAnimationDuration); 211 scaleAnimatorY.setStartDelay(animationDelay); 212 scaleAnimatorY.addListener(new AnimatorListenerAdapter() { 213 @Override 214 public void onAnimationEnd(Animator animation) { 215 view.setScaleY(1.0f); 216 view.setLayerType(View.LAYER_TYPE_NONE, null); 217 } 218 }); 219 220 animators.add(scaleAnimatorY); 221 } 222 223 /** 224 * Collapse a view out by scaling it from its current scaled value to 0. 225 */ 226 public static void addCollapseOutAnimators(List<Animator> animators, final View view, 227 int animationDelay) { 228 view.setLayerType(View.LAYER_TYPE_HARDWARE, null); 229 230 final ObjectAnimator scaleAnimatorY = ObjectAnimator.ofFloat(view, View.SCALE_Y, 231 view.getScaleY(), 0); 232 scaleAnimatorY.setInterpolator(sDecelerateQuintInterpolator); 233 scaleAnimatorY.setDuration(sAnimationDuration); 234 scaleAnimatorY.setStartDelay(animationDelay); 235 scaleAnimatorY.addListener(new AnimatorListenerAdapter() { 236 @Override 237 public void onAnimationEnd(Animator animation) { 238 view.setScaleY(0); 239 view.setLayerType(View.LAYER_TYPE_NONE, null); 240 } 241 }); 242 243 animators.add(scaleAnimatorY); 244 } 245 246 /** 247 * Collapse a view out by scaling it from its current scaled value to 0. 248 * The animators are expected to run immediately without a start delay. 249 */ 250 public static void addCollapseOutAnimators(List<Animator> animators, final View view) { 251 addCollapseOutAnimators(animators, view, 0 /* animation delay */); 252 } 253 254 255 256 /** 257 * Fly a view out by moving it vertically off the bottom of the screen. 258 */ 259 public static void addFlyOutAnimators(List<Animator> animators, 260 final View view, int startTranslation, int endTranslation, int animationDelay) { 261 addYTranslationAnimators(animators, view, startTranslation, endTranslation, 262 animationDelay, null); 263 } 264 265 public static void addFlyOutAnimators(List<Animator> animators, final View view, 266 int startTranslation, int endTranslation) { 267 addFlyOutAnimators(animators, view, startTranslation, 268 endTranslation, 0 /* animation delay */); 269 } 270 271 public static void addSlideInFromRightAnimators(List<Animator> animators, final View view, 272 int startTranslation, int animationDelay) { 273 addXTranslationAnimators(animators, view, startTranslation, 0, animationDelay, null); 274 addFadeAnimators(animators, view, 0, 1.0f, animationDelay); 275 } 276 277 /** 278 * Slide a view out of view from the start to end position, fading the view out as it 279 * approaches the end position. 280 */ 281 public static void addSlideOutAnimators(List<Animator> animators, final View view, 282 int startTranslation, int endTranslation, int animationDelay) { 283 addFadeAnimators(animators, view, view.getAlpha(), 0, animationDelay); 284 addXTranslationAnimators(animators, view, startTranslation, endTranslation, 285 animationDelay, null); 286 } 287 288 /** 289 * Slide a view out of view from the start to end position, fading the view out as it 290 * approaches the end position. The animators are expected to run immediately without a 291 * start delay. 292 */ 293 public static void addSlideOutAnimators(List<Animator> animators, final View view, 294 int startTranslation, int endTranslation) { 295 addSlideOutAnimators(animators, view, startTranslation, 296 endTranslation, 0 /* animation delay */); 297 } 298 299 /** 300 * Add animations to fade a view from the specified start alpha value to end value. 301 */ 302 public static void addFadeAnimators(List<Animator> animators, final View view, 303 float startAlpha, final float endAlpha, int animationDelay) { 304 if (startAlpha == endAlpha) { 305 return; 306 } 307 308 view.setLayerType(View.LAYER_TYPE_HARDWARE, null); 309 view.setAlpha(startAlpha); 310 311 final ObjectAnimator fadeAnimator = ObjectAnimator.ofFloat(view, View.ALPHA, 312 view.getAlpha(), endAlpha); 313 fadeAnimator.setInterpolator(sDecelerateQuintInterpolator); 314 fadeAnimator.setDuration(sAnimationDuration); 315 fadeAnimator.setStartDelay(animationDelay); 316 fadeAnimator.addListener(new AnimatorListenerAdapter() { 317 @Override 318 public void onAnimationEnd(Animator animation) { 319 view.setAlpha(endAlpha); 320 view.setLayerType(View.LAYER_TYPE_NONE, null); 321 } 322 }); 323 animators.add(fadeAnimator); 324 } 325 326 /** 327 * Add animations to fade a view from the specified start alpha value to end value. 328 * The animators are expected to run immediately without a start delay. 329 */ 330 public static void addFadeAnimators(List<Animator> animators, final View view, 331 float startAlpha, final float endAlpha) { 332 addFadeAnimators(animators, view, startAlpha, endAlpha, 0 /* animation delay */); 333 } 334 } 335