Animation / AnimationListener / Interpolator / Translate

Simple Animation – Part 2

In the previous article we got a simple animated transition working. You can get the previous source code here.

It is worth pointing out that there are some stock animations available to us but, for some reason, in Android 2.3.3 only four are actually made public so that we can actually use them in our apps. The good news is that they are visible in AOSP, and there are some useful techniques that we can learn by studying some of the animations used by Android itself.

The next thing we can do is to add some dynamics to the movement of our transition. We can do this by applying an interpolator, which defines a rate of change for our animation. Fortunately for us, more of the stock Android interpolators are made public, and so we can use them in our apps.

By default, Android applies an accelerate / decelerate interpolator which starts slow, speeds up towards the middle and slows down towards the end. While this may not be obvious from looking at the transition that we’ve done so far, let’s apply a linear interpolator instead, which will move at a constant rate. To do this we’ll need to change each the animations that we defined in the previous article. The change is identical for each,so I’ll just show the change to res/anim/slide_in_left.xml:



We have simply added the attribute on line 5 to specify a linear interpolator. To see a direct comparison, try only applying this change to the slide_in_left and slide_out_left animations. That way the transition from FirstActivity to SecondActivity will use a linear interpolator, whereas the reverse transition will use the default. Although the difference is subtle, the linear transition stops abrubtly whereas the accelerate / decelerate transition is a bit smoother.

Another interpolator worth a look is the overshoot interpolator. This overshoots the specified end position and then reverses back to finish at the specified end position:



The public interpolators that are exposed by Android are:

  • accelerate_decelerate_interpolator
    This starts slowly, speeds up, and then slows down towards the end
  • accelerate_interpolator
    This starts slowly and speeds up as the animation progresses
  • anticipate_interpolator
    This starts behind the specified start point, a little bit like pulling back a slingshot
  • anticipate_overshoot_interpolator
    This combines an anticipate at the start with an overshoot at the end
  • bounce_interpolator
    This rebounds from the end position a couple of times, with each bounce diminishing
  • cycle_interpolator
    This repeats the animation
  • decelerate_interpolator
    This slows down as the animation progresses
  • linear_interpolator
    This progresses at a uniform rate
  • overshoot_interpolator
    This overshoots the specified end position and then reverses back to finish at the specified end position

These interpolators mat directly to the interpolator classes in the android.view.animation package which implement the Interpolator interface.

What we’ve seen in the previous article, and in this one is will cover much of what we’ll need to do when transitioning between activities, but what about if we want to apply animations to specific views? Suppose that we want to animate the “Next” button after we click it, and before we begin navigating to SecondActivity. First let’s define a new animation in res/anim/fade_out.xml:



Actually, this is a publicly available animation which you can access through android.R.anim.fade_out.xml, but I’ve used it here for explanation and so that we can tweak things later.

It is different to the animations that we’ve done thus far in that it is an alpha animation as opposed to the translation animations that we’ve encountered up to now. An alpha animation will transition the transparency of the object that it is being applied to. In this case we are transitioning from a value of 1.0 (opaque) to 0.0 (transparent) using an accelerate interpolator, over a medium duration.

Let’s apply this to the button. In FirstActivity:

@Override
protected void onCreate( Bundle savedInstanceState )
{
	super.onCreate( savedInstanceState );
	setContentView( R.layout.first );
	final Button button = (Button)findViewById( R.id.NextButton );
	button.setOnClickListener( new OnClickListener()
	{
		@Override
		public void onClick( View v )
		{
			Animation animation = AnimationUtils.loadAnimation( FirstActivity.this, R.anim.fade_out );
			button.startAnimation( animation );
			startActivity( new Intent( FirstActivity.this, SecondActivity.class ) );
			overridePendingTransition( R.anim.slide_in_left, R.anim.slide_out_left );
		}
	});
}

The first thing that we’ve done here is to use a variable to store the button object as we’ll need to use it later. Then we create an animation object which we inflate from the animation that we’ve just defined, and we start that animation on the button. If we try this, it doesn’t seem to work. The button is not fading as we want it to, it seems to be jumping to the right of the screen, rather than fading. This is because the overridePendingTranslation is actually overriding the animation that we’ve just started. Actually, as I defined what we were trying to do, I said I wanted to fade the button before making the transition. We can use an AnimationListener to do this:

@Override
protected void onCreate( Bundle savedInstanceState )
{
	super.onCreate( savedInstanceState );
	setContentView( R.layout.first );
	final Button button = (Button)findViewById( R.id.NextButton );
	button.setOnClickListener( new OnClickListener()
	{
		@Override
		public void onClick( View v )
		{
			Animation animation = AnimationUtils.loadAnimation( FirstActivity.this, R.anim.fade_out );
			animation.setAnimationListener( new AnimationListener()
			{				
				@Override
				public void onAnimationStart( Animation animation ) {}
				
				@Override
				public void onAnimationRepeat( Animation animation ) {}
				
				@Override
				public void onAnimationEnd( Animation animation )
				{
					startActivity( new Intent( FirstActivity.this, SecondActivity.class ) );
					overridePendingTransition( R.anim.slide_in_left, R.anim.slide_out_left );
				}
			} );
			button.startAnimation( animation );
		}
	});
}

An AnimationListener has callbacks for the animation starting, the animation ending, and when the animation repeats. All we have done is to move our code to navigate to SecondActivity to the animation end callback, and everything works almost as we expect.

The only slight glitch here is that as soon as the button has faded and the activity navigation begins, the button re-appears. A quick solution is to add button.setVisibility( View.INVISIBLE ); to onAnimationEnd. This now has a knock on effect that when re return to FirstActivity, the button is no longer visible. This can be remedied by adding:

@Override
protected void onResume()
{
	findViewById( R.id.NextButton ).setVisibility( View.VISIBLE );
	super.onResume();
}

In the final part of the Simple Animation tutorial we’ll cover some other animation types.

As ever, the final source code for this project can be found here.

© 2011, Mark Allison. All rights reserved.

Copyright © 2011 Styling Android. All Rights Reserved.
Information about how to reuse or republish this work may be available at http://blog.stylingandroid.com/license-information.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.