Wednesday, June 1, 2011

Introducing ViewPropertyAnimator

The property animation capabilities that are in Android 3.0 (described in the article Animation in Honeycomb) represent a good building block for powerful animation capabilities in the platform. But we're not stopping there. One minor addition to the platform was added in the 3.1 release, ViewPropertyAnimator. This class allows very easy and efficient one-liner animations of some of the new View properties like alpha, rotation, and scaleX/Y.

Check out the Introducing ViewPropertyAnimator article on the Android developer blog for more about this new API. In the meantime, enjoy this teaser trailer that shows the demo described in the article. The interesting part is the code (shown in the article, not here). And the soundtrack, of course: turn up your speakers.

24 comments:

JarrettV said...

Are these animations utilizing renderscript?

Chet Haase said...

Nope - no relationship between this (which uses regular buttons and the new ViewPropertyAnimator API) and Renderscript (which is about doing performance- or graphics-intensive operations).
For one thing, ViewPropertyAnimator is specific to a handful of properties on the View class, which has nothing to do with Renderscript.
You could use the other Animator classes to run animations which changed values used by Renderscript scripts. But this is probably not the best way to use Renderscript, performance-wise - you probably want to stay at the RS level once you're there.

Filipe said...

Hello Chet,

Thank you for the nice article and keep up the good work.


  PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("x", 50f);
    PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("y", 50f);

(guess it's pvhY right there, oh the joy of being fussy) :P

shez said...

It looks like each call to View.animate() returns the same animator object, which has a single duration, is that correct?

Is it possible to extend the API so that I can specify separate duration for each property? like:

view.animate().x(50, 300).alpha(0, 500);

this would animate x to 50, and simultaneously make it transparent, but the x property would move in 300ms and the transparency would take 500ms. I guess it could still use the same underlying timer, but the animator would know the total animation length is 500ms and it would just interpolate x to complete in 300ms. Is something like this possible as a future enhancement?

shez said...

or maybe even a start delay for each property, so:

view.animate().x(50, 0, 300).alpha(0, 150, 500);

so the alpha interpolation begins somewhere between the x animation and lasts for 500ms (so total animation length is 650ms).

Animation Degree said...

You could use the other Animator classes to run animations which changed values used by Renderscript scripts.

Chet Haase said...

@Filipe: Thanks for catching the typo. No matter how many times I edit and revise, there will always be errors. It's like software.

@shez: No, for two reasons. First, it's intended to be a very simple API that extends a simple assignment (x = 50) and makes it an animated assignment. Complicating the API with various optional parameters could end up making it more confusing to use for the very simple cases it's meant to address.

Second, it uses a single Animator internally, which cannot have individual delays and durations for the various properties it's animating. Turning that single Animator into one that knows how to animate individual animations with different attributes severely complicates it, especially when you consider that the different animations would be at different points in their interpolation curves. We'd basically end up creating an entirely new animation system inside this class to handle that functionality.

That's what the other Animator classes are there for. They're not hard to use either, and are intended to provide the flexibility that you need for any animation need. This class is all about simplicity.

shez said...

@Chet: Thanks for clarifying :)

Steve Gehrman said...

add this to the manifest.

android:hardwareAccelerated="true"

Rotate is less pixelated. I was kind of disappointed looking at the demo at first, but after adding that, it animates better with less jaggies.

Chet Haase said...

@Steve: Actually, the biggest factor in the quality of the demo is the video itself. Between the capture card and the software I used to import/export the eventual screencast, there were a lot of dropped frames that make it seem pretty choppy. It looks way better on my tablet than it does on the video.
Enabling hardware acceleration is a good idea in general, just not something I considered for this demo because it wasn't the point I was trying to get across; I just wanted to show stuff moving on the screen.

Steve Gehrman said...

I didn't watch the video. Just ran it after getting 3.1 on my tab yesterday. Looks better with the flag set when doing the rotation. Probably should be mentioned in the article to get the best results. After the first run, I saw the jagged pixels and almost gave up on it thinking there was something wrong, but then remembered to check for that flag and faith was restored.

Steve Gehrman said...
This comment has been removed by the author.
Steve Gehrman said...

Trying to chain animations together, but I see an odd behavior. Say I scalex to 2, that animation ends and I scalex back to 1, that scalex(1) call inside onAnimationEnd triggers an onAnimationCancel on that first animation. Isn't the first animation done once I get the onAnimationEnd? That's confusing. On 3.1 tab.

Chet Haase said...

@Steve: I'll check out the chaining issue, see if there's a bug there. To your earlier [redacted] question: onAnimationEnd() provides a single point through which all animations pass. This simplifies things if you always want to know when something ends (to trigger something else), versus having to implement both and end and cancel listener. If you have special logic that you want to run only on an end, just track whether the animation was canceled prior to the end call.

Steve Gehrman said...

@Chet: I rewrote all my code to use the ObjectAnimator old api with animation sets to do the chaining. I think that's what I should be using. Using the new API, I could not get the code working to sequentially trigger animations. I couldn't figure out how to handle the cancel state. If I start one animation, then in the end call back start the second, I get the cancel call back and now there doesn't seem to be a way of knowing if that cancel came from starting the second animation, or if it came from the user clicking the button again to start a new animation. Kind of tough to expain, but you could probably make a little modification to your demo to try for yourself. The AnimationSets are better anyway, so not important.

Chet Haase said...

@Steve: I think that's the right thing to do for your situation. VPA was not intended to replace ObjectAnimator, but rather to be an easy utility for animating changes to those specific properties. When you get into more general situations (like chaining animations), ObjectAnimator may be a better choice.
I should look into the cancel thing. VPA does auto-cancel prior to starting an animation on a property (to ensure that you're not animating a given property to different values). Maybe there's a side effect there that causes an ended animation to get canceled anyway.

Emil Sjölander said...

Hi Chet,

This may not be something that is possible with the ViewPropertyAnimator but i am hoping you can point me in the correct direction.

How would you suggest i go by animating a ViewGroup without animating any of the child views contained in the ViewGroup?

The reason i want to do this is because i want to scale a ViewGroup with an animation and when the ViewGroup reaches a size that is big enough to accommodate another child, one will be animated into the ViewGroup.

Chet Haase said...

@Emil: Yep, that's not a job for ViewPropertyAnimator, which just animates the transform and alpha properties of a View (or a ViewGroup). You should check out LayoutTransition. Take a look at the applications in ApiDemos/Animations that use LayoutTransition to see what it does and how to use it.

Eyahn said...

Hi there, i was wondering... is it possible to animate width and heigth of a view? I am trying with a LinearLayout, but there are no getter and setter for those value (no luck with LayoutParams as well).
Thanks in advance


E

Chet Haase said...

@Eyahn: width/height are not properties, but rather side-effects of left/right/top/bottom. So if you want to animate the dimensions of an object, animate its left/right/top/bottom bounds. This is what LayoutTransition does when it animates the sizes of the objects that change between the start/end of that layout operation.
Note that there is no protection against the layout that an object lives in also changing that object's left/right/top/bottom; those properties are really owned by the layout. So either ensure that a layout will not run during that animation or find a different way to achieve what you want. For example, you can animate the layout params of the object (this is the most correct way to go, although running a full layout every frame of the animation to react to these changes can be expensive).

kk said...

If I try to create something like Google Music landscape UI Album cover view where a stack of Album pictures can roll from side to side. Does ViewPropertyAnimator API can perform such 3D type of Animator? I tried to search around but there is not much information that talks about how to build such Album view.

The Finisher said...

Hahaha

Thank you for your articles. They are very informative and to the point, not to mention witty. Cheers!

Moritz Post said...

Hi Chat. Just wanted to mention that i also run into the described problem when chaining VPA animations. When i scale up and in onAnimationEnd scale down again it works for the first time. When i want to call that chain again, the upscalling does not work anymore.

Anonymous said...

Chet, VPA optimization makes sense when you want to animate multiple properties per view.

But what if I want to run buch of VPAs in parallel something like AnimatorSet.playTogether.

How do I do that with VPA?