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!