content: package com.rsen.github.easyandroidanimations;
  
  import android.animation.Animator;
  import android.animation.AnimatorListenerAdapter;
  import android.animation.AnimatorSet;
  import android.animation.ObjectAnimator;
  import android.animation.TimeInterpolator;
  import android.view.View;
  import android.view.ViewGroup;
  import android.view.animation.AccelerateDecelerateInterpolator;
  
  /**
   * This animation causes the view to bounce by translating up and down for a
   * customizable number of times before returning to its original position.
   * 
   * @author SiYao
   * 
   */
  public class BounceAnimation extends Animation {
  
  	float bounceDistance;
  	int numOfBounces, bounceCount = 0;
  	TimeInterpolator interpolator;
  	long duration;
  	AnimationListener listener;
  
  	/**
  	 * This animation causes the view to bounce by translating up and down for a
  	 * customizable number of times before returning to its original position.
  	 * 
  	 * @param view
  	 *            The view to be animated.
  	 */
  	public BounceAnimation(View view) {
  		this.view = view;
  		bounceDistance = 20;
  		numOfBounces = 2;
  		interpolator = new AccelerateDecelerateInterpolator();
  		duration = DURATION_LONG;
  		listener = null;
  	}
  
  	@Override
  	public void animate() {
  		long singleBounceDuration = duration / numOfBounces / 4;
  		if (singleBounceDuration == 0)
  			singleBounceDuration = 1;
  		final AnimatorSet bounceAnim = new AnimatorSet();
  		bounceAnim.playSequentially(ObjectAnimator.ofFloat(view,
  				View.TRANSLATION_Y, bounceDistance), ObjectAnimator.ofFloat(
  				view, View.TRANSLATION_Y, -bounceDistance), ObjectAnimator
  				.ofFloat(view, View.TRANSLATION_Y, bounceDistance),
  				ObjectAnimator.ofFloat(view, View.TRANSLATION_Y, 0));
  		bounceAnim.setInterpolator(interpolator);
  		bounceAnim.setDuration(singleBounceDuration);
  
  		ViewGroup parentView = (ViewGroup) view.getParent(), rootView = (ViewGroup) view
  				.getRootView();
  		while (!parentView.equals(rootView)) {
  			parentView.setClipChildren(false);
  			parentView = (ViewGroup) parentView.getParent();
  		}
  		rootView.setClipChildren(false);
  		bounceAnim.addListener(new AnimatorListenerAdapter() {
  
  			@Override
  			public void onAnimationEnd(Animator animation) {
  				bounceCount++;
  				if (bounceCount == numOfBounces) {
  					if (getListener() != null) {
  						getListener().onAnimationEnd(BounceAnimation.this);
  					}
  				} else {
  					bounceAnim.start();
  				}
  			}
  		});
  		bounceAnim.start();
  	}
  
  	/**
  	 * @return The maximum bounce distance.
  	 */
  	public float getBounceDistance() {
  		return bounceDistance;
  	}
  
  	/**
  	 * @param bounceDistance
  	 *            The maximum bounce distance to set.
  	 * @return This object, allowing calls to methods in this class to be
  	 *         chained.
  	 */
  	public BounceAnimation setBounceDistance(float bounceDistance) {
  		this.bounceDistance = bounceDistance;
  		return this;
  	}
  
  	/**
  	 * @return The number of bounces.
  	 */
  	public int getNumOfBounces() {
  		return numOfBounces;
  	}
  
  	/**
  	 * @param numOfBounces
  	 *            The number of bounces to set.
  	 * @return This object, allowing calls to methods in this class to be
  	 *         chained.
  	 */
  	public BounceAnimation setNumOfBounces(int numOfBounces) {
  		this.numOfBounces = numOfBounces;
  		return this;
  	}
  
  	/**
  	 * @return The interpolator of the entire animation.
  	 */
  	public TimeInterpolator getInterpolator() {
  		return interpolator;
  	}
  
  	/**
  	 * @param interpolator
  	 *            The interpolator of the entire animation to set.
  	 */
  	public BounceAnimation setInterpolator(TimeInterpolator interpolator) {
  		this.interpolator = interpolator;
  		return this;
  	}
  
  	/**
  	 * @return The duration of the entire animation.
  	 */
  	public long getDuration() {
  		return duration;
  	}
  
  	/**
  	 * @param duration
  	 *            The duration of the entire animation to set.
  	 * @return This object, allowing calls to methods in this class to be
  	 *         chained.
  	 */
  	public BounceAnimation setDuration(long duration) {
  		this.duration = duration;
  		return this;
  	}
  
  	/**
  	 * @return The listener for the end of the animation.
  	 */
  	public AnimationListener getListener() {
  		return listener;
  	}
  
  	/**
  	 * @param listener
  	 *            The listener to set for the end of the animation.
  	 * @return This object, allowing calls to methods in this class to be
  	 *         chained.
  	 */
  	public BounceAnimation setListener(AnimationListener listener) {
  		this.listener = listener;
  		return this;
  	}
  
  }
  