Android Animations Tutorial 4: Programming View Animations

Previously I showed you how to create View animations using XML resources. I just wrote a few lines of actual Java code that was only needed to start the animation. In this post I will show you how you can achieve the same effect using the View Animation API. This gives you more freedom because you can dynamically specify the start and end values of your animation. To see where this is useful, remember the zoom animation I presented in the first post in this series. The image was zoomed from the top left corner. A more natural animation would start with the animated image having the exact same screen coordinates as the thumbnail. This would give the impression of the thumbnail expanding to fill the screen. I will show you how this is done at the end of this tutorial. But first let’s look at the fundamentals of programming View Animations.

The Fundamentals: TranslateAnimation

Every type of animation tag that you can set in the resource files has a corresponding class. All the animation classes are defined in the android.view.animation package and inherit from the Animation class. Take the translate animation which I introduced in part 2 of this series. The corresponding class is the TranslateAnimation class. There are two constructor of TranslateAnimation which are useful for creating your own translate animations in the code.

TranslateAnimation(float fromXDelta, float toXDelta,
                   float fromYDelta, float toYDelta)

TranslateAnimation(int fromXType, float fromXValue,
                   int toXType, float toXValue,
                   int fromYType, float fromYValue,
                   int toYType, float toYValue)

As you can see, all the translation-specific parameters of the animation are passed in the constructor. The parameters correspond directly to the values that you would otherwise write in the XML file. There are, in fact, no methods to change these parameters later on. The first constructor takes the start and end position of the translation in pixels. The second constructor allows the positions to be specified in different units, either in pixels, in percentage of the view’s size or in percentage of the parent’s size. Let’s look at the following example.

public void doSomeAnimation() {
  View animatedView = findViewById(R.id.animatedView);
  TranslateAnimation animation = new TranslateAnimation(
    Animation.ABSOLUTE, -10,
    Animation.ABSOLUTE, -10,
    Animation.RELATIVE_TO_SELF, 0.5,
    Animation.RELATIVE_TO_PARENT, 0.8);
  animatedView.startAnimation(animation);
}

This will animate a translation of the view starting 10 pixels to the top-left and ending at 50% of the view’s width and 80% of the parent’s height. As you can see, there is a one-to-one correspondence between the XML specification of the animation and the constructor of the TranslateAnimation class.

Other Animation Classes

Here is a complete list of classes that extend from the Animation class.

AlphaAnimation
RotateAnimation
ScaleAnimation
TranslateAnimation
AnimationSet

All of these correspond the the animation XML tags <alpha>, <rotate>, <scale>, <translate>,  and <set>. All of these have constructors that take the animation parameters specific to the type of animation and that can be specified in the XML file. Here is a quick run-down of all the constructors.

AlphaAnimation(float fromAlpha, float toAlpha)

RotateAnimation(float fromDegrees, float toDegrees)

RotateAnimation(float fromDegrees, float toDegrees,
                float pivotX, float pivotY)

RotateAnimation(float fromDegrees, float toDegrees,
                int pivotXType, float pivotXValue,
                int pivotYType, float pivotYValue)

ScaleAnimation(float fromX, float toX,
               float fromY, float toY)

ScaleAnimation(float fromX, float toX,
               float fromY, float toY,
               float pivotX, float pivotY)

ScaleAnimation(float fromX, float toX,
               float fromY, float toY,
               int pivotXType, float pivotXValue,
               int pivotYType, float pivotYValue)

TranslateAnimation(float fromXDelta, float toXDelta,
                   float fromYDelta, float toYDelta)

TranslateAnimation(int fromXType, float fromXValue,
                   int toXType, float toXValue,
                   int fromYType, float fromYValue,
                   int toYType, float toYValue)

AnimationSet(boolean shareInterpolator)

If you have read the overview of View Animations then these constructors are pretty self explanatory and I will not repeat the meaning of all the arguments here. Just remember that you can only set the parameters of the animation in the constructor. Let’s say you have created a ScaleAnimation using only the first constructor.

ScaleAnimation scale = new ScaleAnimation(0.0f, 1.0f, 0.0f, 1.0f);

// no way to change the pivot point after creating the object

If you wanted to add a pivot point afterwards, you will find that this is not possible. You would have to create a new ScaleAnimation object by calling one of the other constructors.

Also note that there are no getter methods that would allow you to query the values which are related to the scale animation.

The AnimationSet

The AnimationSet animation class is different from the others in that it does not define an animation on its own. Instead you have to add Animation objects to it.

void addAnimation(Animation a)

This method adds an Animation object to the AnimationSet. Note that the order in which the animations are added is relevant. If you add a rotation before adding a translation, the rotation will be applied before the translation. This will have an influence on where the view will apper on the screen.

An Animation Example

Let’s illustrate everything with a simple example. Let’s say we want to create a view that spins onto the screen, just like a newspaper would spin into view in the old movies. We want to combine a rotate animation, a scale animation and, for good taste, an alpha animation as well.

We start by creating the rotation. The rotation should be around the view’s centre, so we need to specify the pivot relative to the view’s dimensions.

RotateAnimation rotate
  = new RotateAnimation(0.0f, 1080.0f,
                        Animation.RELATIVE_TO_SELF, 0.5f,
                        Animation.RELATIVE_TO_SELF, 0.5f);

The next part of the animation is the scale animation. This should simply scale the animation from 0 to 100%.

ScaleAnimation scale
  = new ScaleAnimation(0.0f, 1.0f, 0.0f, 1.0f,
                       Animation.RELATIVE_TO_SELF, 0.5f,
                       Animation.RELATIVE_TO_SELF, 0.5f);

Finally we create the alpha animation. This is as simple as the scale animation. We want to change the alpha value from 0 to 1.

AlphaAnimation alpha = new AlphaAnimation(0.0f, 1.0f);

We need an animation set to put it all together. For this we create an AnimationSet object and add all our animations to it.

AnimationSet set = new AnimationSet(true);
set.addAnimation(rotate);
set.addAnimation(scale);
set.addAnimation(alpha);
set.setDuration(2000);

Now all we have to do is start the animation on our view.

View newspaper = findViewById(R.id.newspaper);
newspaper.startAnimation(set);

The result can be seen below.

A spinning newspaper animation. Click the image to view the animation.

Common Methods: The Animation Class

While the parameters specific to each animation type can only be set in the constructor, the common parameters are set and queried using standard setters and getters.

void setDuration(long durationMillis)
long getDuration()

void setRepeatCount(int repeatCount)
int getRepeatCount()

void setRepeatMode(int repeatMode)
int getRepeatMode()

void setFillAfter(boolean fillAfter)
boolean getFillAfter()

These are the setters and getters for the duration of the animation, for the repeat count, for the repeat mode and for the fillAfter property.

Controlling the Start Time

void setStartTime(long startTimeMillis)

Previously we always started the animation by calling View.startAnimation. In reality the startAnimation method is a convenience method that sets the start time of the animation, and invalidates both the view and the parent view so that the animation is displayed. The methods setStartTime allows fine grained control over the start time of the animation. You can set the start time of the animation with the setStartTime method. The method takes the absolute time in milliseconds. But beware: you should not use the System time; instead you have to use the Animation Time. Here is an example that starts the animation immediately.

View view = findViewById(R.id.animated_view);
Animation anim = new ScaleAnimation(0.0, 1.0, 0.0, 1.0);
anim.setStartTime(AnimationUtils.currentAnimationTimeMillis());
view.setAnimation(anim); 
view.invalidate();
((View) getParent()).invalidate();

Instead of passing the time, you can also pass the START_ON_FIRST_FRAME constant.

View view = findViewById(R.id.animated_view);
Animation anim = new ScaleAnimation(0.0f, 1.0f, 0.0f, 1.0f);
anim.setStartTime(START_ON_FIRST_FRAME);
view.setAnimation(anim);

The difference between the two alternatives is only subtle and probably only visible for short animations, where the animation only lasts a few frames. If you set the start time to the current time, then some time will have elapsed before the first frame of the animation is shown. For short animations this means that the view will not be seen to originate from the starting position but the first frame will already show an intermediate state of the view. To avoid this START_ON_FIRST_FRAME tells the animation to wait until the first frame of the animation is drawn.

Two convenience methods can be used to start the animation at the current time.

void start()
void startNow()

The method start is equivalent to calling setStartTime(START_ON_FIRST_FRAME) and the method startNow is the same as setStartTime(AnimationUtils.currentAnimationTimeMillis()).

Note: Although not documented, you should set the start time of the animation by calling setStartTime, start or startNow before you call setAnimation on the view.

You also can query if the animation has started or has ended.

boolean hasStarted()
boolean hasEnded()

Setting Interpolators

The Animation class has a method to set the interpolator for the animation.

void setInterpolator(Interpolator i)

You can use any of the interpolators introduced in the last post. To set the accelerate decelerate interpolator, simply do the following.

anim.setInterpolator(new AccelerateDecelerateInterpolator());

An Example

Let’s design an animation that can be used for dismissing a view. We want to imitate the view being chucked away to the side. The advantage of programming the animation instead of specifying it as an XML resource is that we can dynamically set the start and end position of the view. We want the view to start at the location of an image that is on the screen.

Rect imageRect = new Rect();
Rect globalRect = new Rect();
Point globalOffset = new Point();

ImageView image = (ImageView)findViewById(R.id.image);
View container = findViewById(R.id.container);

image.getGlobalVisibleRect(imageRect);
container.getGlobalVisibleRect(globalRect,globalOffset);
imageRect.offset(-globalOffset.x, -globalOffset.y);

After this the imageRect holds the bounding box of the image, relative to the container. We are going to move the image sideways by twice the width of the image.

Animation translate
  = new TranslateAnimation(imageRect.left,
                           imageRect.left+2*imageRect.width(),
                           imageRect.top,
                           imageRect.top);
translate.setInterpolator(new AnticipateInterpolator());

The AnticipateInterpolator creates the effect throwing the view. We will also scale the view up by a factor of 2 and make it transparent.

Animation scale
  = new ScaleAnimation(1.0f, 2.0f, 1.0f, 2.0f,
                       Animation.RELATIVE_TO_SELF, 0.5f,
                       Animation.RELATIVE_TO_SELF, 0.5f);
scale.setInterpolator(new AccelerateInterpolator());
Animation alpha = new AlphaAnimation(1.0f, 0.0f);
alpha.setInterpolator(new LinearInterpolator());

An animation set will wrap all the different animations together. Because every animation has its own interpolator we have to set shareInterpolator to false.

AnimationSet set = new AnimationSet(false);
set.addAnimation(translate);
set.addAnimation(scale);
set.addAnimation(alpha);

Finally we hide the image and show the animated view before we start the animation.

image.setVisibility(View.INVISIBLE);

ImageView animatedImage = (ImageView) findViewById(R.id.animatedImage);
animatedImage.setImageDrawable(image.getDrawable());
animatedImage.setVisibility(View.VISIBLE);

set.setFillAfter(true);
set.setDuration(300);
set.start();
animatedImage.setAnimation(set);

Animation of a view being thrown towards the user. Click on the image to view the animation.

So here is a gif showing the animation in practice.

Next time I will be talking about creating your own interpolators, and I might also squeeze in a post about understanding the fillAfter, fillBefore and fillEnabled properties, before I finally move on to property animations.

Follow the author

16 Comments

  1. Alex

    Thanx a lot for such tuts, but it wouldn’t be bad if you give a ref to source codes, at least for examples…

    Reply
  2. Mikail (Post author)

    @Alex I fully agree. I am planning to create a git repository containing all the examples from my tutorials. I have some tidying up to do first but as soon as that is done, I will post the link here.

    Reply
  3. Alex

    just can’t get what is container?

    Reply
  4. Mikail (Post author)

    @Alex The container is the layout that contains the ImageView. You are right that this isn’t very clear at the moment. I will include the layout XML in the tutorial tonight. Then, hopefully, everything will become clear.

    Reply
  5. Alex

    Thanx!!!

    Reply
  6. Khang

    If the image on the screen is smaller than its raw, the animated image will be bigger. How can I set exact rect of the animated image ?

    Reply
    1. Mikail (Post author)

      The size of the image displayed on the screen is usually determined by the layout. Given that you have an ImageView called image you can set the width and height of the image directly using


      image.getLayoutParams().width = 20;
      image.getLayoutParams().height = 20;

      If you know the Bitmap that is contained in the ImageView then you can do something like


      image.getLayoutParams().width = bitmap.getWidth();
      image.getLayoutParams().height = bitmap.getHeight();

      You should do this before the views are laid out on the screen. Otherwise you will need to issue a call to image.requestLayout()

      Reply
  7. Fusiller

    Excellent tut ! So clear and precise.
    Thanks a LOT.
    Alex

    Reply
  8. Deepak Ror

    Great,,,,,,,,,,,,,,,,,,,,,,,,.

    its clear everythink in animation android.

    Reply
  9. dario

    Thanks very mach for your lesson!
    Can I ask a question please?
    When I maneg my animation, a traslation to move a view from point A to point B.
    The animation works well but at the end of animation when the view arrive to point B, it came back immediatly to the point A.
    How can I do for to fix the view at the point B?

    Reply
    1. Mikail (Post author)

      If you have a view that is managed by a layout then the position of the view will return to its normal location. This is expected behaviour. In order to change the position of the view permanently you need to tell the layout where the view should stay after the animation has finished.

      The normal way to do this is by using the AnimationListener and override the onAnimationEnd method. I have written a tutorial on the AnimatorListener for Property Animations. The details are somewhat different to View Animations but the general concept is the same.

      Reply
  10. SeekerOfProperAPIDoc

    This is probably the only place on the net which actually gives some explanation on how setStartTime/setAnimation works, kudos.

    However, when I tried to actually play around with the code in “Controlling the Start Time”, some problems came up:

    1.ScaleAnimation does not have a constructor which takes 2 floats (It’s a Context and an AttributeSet).
    This is easy, just use the one with 4 floats.

    2.view.invalidate() does not animate the given view.
    I tried to run the given code in a button’s onClick callback,
    but the button does not scale.
    Following a quick check on the source code, I had to resort to view.getParent.invalidate() to actually see the animation.
    If view.invalidate() actually works I’d like to know what I did wrong.

    Reply
    1. Mikail (Post author)

      Thanks for pointing out these issues. You are completely correct. The ScaleAnimation should be constructed with 4 floats instead of two.

      Your second point is also correct. The parent view needs to be invalidated. If I had read my own tutorial carefully then I would have seen that startAnimation does exactly that. It is also pointed out in the official documentation of the setAnimation method.

      I have updated the tutorial and fixed the mistakes.

      Reply
      1. SeekerOfProperAPIDoc

        Hi Mikali, thanks for the quick reply.

        I was wondering if you can make a post about, or give me some references on, how the animations work in terms of event flow, like “A triggers B, which in turn calls C, D…”? Specifically, the documentation on setStartTime mentioned “the first time getTransformation is called “, but it did not really say when that can happen. How does it really work? Now that we have newer APIs like AnimatorSet (Not AnimationSet), ViewPropertyAnimator, this probably has little importance, but I’m still curious. (Surprisingly, I can’t find much information about this on the net.)

        Regarding invalidating the parent, I’ve noticed some inconsistencies in the framework. When I use no layer on the View at all (the default, or setLayerType LAYER_TYPE_NONE), invalidating the parent is needed, however, LAYER_TYPE_SOFTWARE or LAYER_TYPE_HARDWARE both allows me to only invalidate the view to start the animation. This seems to contradict what Chet and Romain mentioned in -I forget which talk- about how drawing works, so I don’t know if it’s a bug.

        BTW eh, there’re 2 occurrence of “new ScaleAnimation(0.0, 1.0)” in the post actually, and you fixed only the latter one, thought you might want to know.

        Thanks again.

        Reply
        1. Mikail (Post author)

          Hi Seeker,

          I did not find a lot of documentation myself about how the animation event flow is implemented. I will definitely consider an in-depth tutorial about that. But it will require me to study the actual Android source code a lot more. I am currently somewhat behind on the tutorials I wanted to write so it might take some time until I will get around to it.

          Thanks for pointing out the remaining error.

          Reply
  11. XRumerTest

    Hello. And Bye.

    Reply

Leave a Comment

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

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>