Saturday, December 17, 2011

Animation Two Ways part 3


This is part 3 in a 3 part post outlining the approach I took in handling an increasing number of animations across multiple Android OS versions.

In part 1 I demonstrated the idea of defining your moving parts (well, Views) in a UI state class.
In part 2 I demonstrated the idea of managing all your animation situations in an animation controller class.

In the final part I will demonstrate how I deal with supporting both the old Animation API in Gingerbread and lower, as well as the new Animation API in Honeycomb and greater.

You might have noticed in part 2 that I created an instance of AnimationHelper.  This is the super class where I define the method signatures for all the animations I want to execute.  I've created it as an abstract class, so I'm not actually doing anything here other than creating the method signatures and holding references to the Views I plan to animate.

public abstract class AnimationHelper {

    protected View mPlaylists;
    protected View mContent;
    protected View mFilmStrip;
    protected Context mCtx;

    public AnimationHelper() {
        super();
    }

    /**
     * Define the animation for raising the film strip
     */
    public void startFilmStripUpAnimation() {
    }

    /**
     * Define the animation for lowering the film strip
     */
    public void startFilmStripDownAnimation() {
    }

Then I create a sub class of AnimationHelper for each OS version I want to support. In this case there are only 2 Animation API's. Gingerbread and lower and Honeycomb and higher. So I create FroyoAnimationHelper and HoneycombAnimationHelper where I implement the methods from the super class.  Here is a snippet from my HoneycombAnimationHelper class showing some of the implementation.  Of course the implementation will look different in the FroyoAnimationHelper since you will use the old Animation API for older versions.

public class HoneycombAnimationHelper extends AnimationHelper {
 
 /**
  * Create a HoneycombAnimationHelper object
  * @param mCtx  Activity context
  * @param parent Parent view containing a playlist and a filmstrip
  */
    public HoneycombAnimationHelper(Context mCtx, View parent) {
  this.mCtx = mCtx;
  this.mContent = parent.findViewById(R.id.content);
  this.mPlaylists = parent.findViewById(R.id.playlists);
  this.mFilmStrip = parent.findViewById(R.id.consumption_videos);
 }

 /* (non-Javadoc)
  * @see com.macadamian.android.ottasee.animation.AnimationHelper#startFilmStripUpAnimation()
  * 
  * Using new Honeycomb ObjectAnimator see animation definition in res/animator/animator_film_strip_up.xml
  */
 @Override
 public void startFilmStripUpAnimation() {
  ObjectAnimator set = (ObjectAnimator) AnimatorInflater.loadAnimator(mCtx, R.animator.animator_film_strip_up);
  set.setTarget(mFilmStrip);
  set.start();
 }

 /* (non-Javadoc)
  * @see com.macadamian.android.ottasee.animation.AnimationHelper#startFilmStripDownAnimation()
  * 
  * Using new Honeycomb ObjectAnimator see animation definition in res/animator/animator_film_strip_down.xml
  */
 @Override
 public void startFilmStripDownAnimation() {
  ObjectAnimator set = (ObjectAnimator) AnimatorInflater.loadAnimator(mCtx, R.animator.animator_film_strip_down);
  set.setTarget(mFilmStrip);
  set.start();
 }

 /* (non-Javadoc)
  * @see com.macadamian.android.ottasee.animation.AnimationHelper#startGridUpAnimation()
  */
 @Override
 public void startGridUpAnimation() {
  ObjectAnimator set = (ObjectAnimator) AnimatorInflater.loadAnimator(mCtx, R.animator.animator_grid_up);
  set.setTarget(mFilmStrip);
  set.addListener(new AnimatorListener() {
   
   @Override
   public void onAnimationStart(Animator animation) {    
    mFilmStrip.findViewById(R.id.the_grid).setVisibility(View.VISIBLE);
    mFilmStrip.findViewById(R.id.film_strip_list).setVisibility(View.GONE);
   }
   
   @Override
   public void onAnimationRepeat(Animator animation) {    
   }
   
   @Override
   public void onAnimationEnd(Animator animation) {
   }
   
   @Override
   public void onAnimationCancel(Animator animation) {    
   }
  });
  set.start();
 }

The beauty of implementing it this way is that in your AnimationControler you can create an AnimationHelper but then check your OS version and instantiate the correct sub class at run time.  Here's a clip from my AnimationControler constructor showing how I did this.

    public AnimationController(Activity ctx, View contentView, UserActionListener userActionListener) {
        this.mCtx = ctx;
        this.mContentView = contentView;
        this.mUserActionListener = userActionListener;

        // Setup animations
        if (VersionCheck.isHoneycombOrGreater()) {
            mAnimHelper = new HoneycombAnimationHelper(mCtx, mContentView);
        } else if (VersionCheck.isFroyoOrGingerbread()) {
            mAnimHelper = new FroyoAnimationHelper(mCtx, mContentView);
        }

        // Create initial UI state (ActionBar, Header shown)
        this.mCurrentUIState = new ConsumptionUIState();
        this.mPreviousUIState = new ConsumptionUIState();

        // Initialise the current UI state
        initUIState();
        mPreviousUIState.copyState(mCurrentUIState);
        // set the previous state to video list closed for the first time
        mPreviousUIState.setShowHeader();
    }

As you can see, by setting up your animations like this we can keep our Activity code nice and clean, support multiple OS versions and make it easier for others to work with your animations code.  I hope this helps you keep your code clean!

2 comments:

  1. This is one of the great post.I like your blog details.This is one of the best post. Thanks for your advice.
    Android app developers

    ReplyDelete
  2. Your blog is attractive adequate and i brand your blog accomplishments color, i anticipate it can calmly to allure the visitors and to access your blog traffics.

    Android developers

    ReplyDelete