4

Jetpack Compose way to animate Android Views

 3 years ago
source link: https://proandroiddev.com/jetpack-compose-way-to-animate-android-views-f5ffe0344a0
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Jetpack Compose way to animate Android Views

final result

In this article we will adopt animate*asState from Jetpack Compose for using with regular Android Views

Working with user interfaces sometimes is not an easy job. On Android you have a bunch of presentation layer architectural patterns like MVP, MVI, MVVM, which are managing a view in a slightly different manner.

But in the end you are just changing a view state, no matter what pattern you are using.

You might want to show or hide something, change color, height, scale or whatever else. The easiest way to do that is just set required value, then invalidate the view and on the next frame Android framework will try to re render your view tree.

There are actually some advantages of this approach:

  1. View tree is always in actual state as you not launching any transitions/animations. If for whatever reason you showed some view, but later in a 100ms you want to hide it — no problem, just call view.setVisibility(View.GONE) and that’s it.
  2. Easy and fast to develop/modify/maintain the code as you don’t need to carry about animations. For whatever input you get from your business logic you are just immediately change view state and everything working as expected.

Here is an example of the UI that is using this approach:

1*UBFKRcS8kRsYlSkU6rVMsw.gif?q=20
jetpack-compose-way-to-animate-android-views-f5ffe0344a0
ui example

Note how chip change its background and text color instantly, without any animation. Probably a code to implement something similar would look like this:

Selectable chip view example

Inside of the bind(isSelected) method we are just changing internal properties of a text view depending on a boolean flag. So we might say that our view has 2 states: selected and not selected.

1*yneShKmR8L4LIgWOQH5s9g.gif?q=20
jetpack-compose-way-to-animate-android-views-f5ffe0344a0
example of our custom tabs implementation working

But what if we want to make this a bit more pleasant for the user?

We could at least try to add a color animation to change background and text color. A main point to take care of here is that the

animation must be cancelable and reversible

This means that if we are currently animating one tab, when another tab is selected we should be able to cancel animation on a first tab, animate it back to un selected state, while animating second tab to selected state. Therefore you should have some kind of animation state.

A Jetpack Compose can be inspiration for this. It introduced a bunch of animate*asState functions which are managing state of the animation internally.

Jetpack Compose animate color as state sample

When you update isSelected property this function will cancel current animation (if any) and animate from current animation state to target state with specific animation spec.

This is cool for compose based UI, but what with “legacy” android views? When adopting compose to your app not all screens can be refactored instantly and you might want to just modify existing view implementation.

Let’s try to achieve something similar with ValueAnimators.

AnimatedColor

An AnimatedColor class with animation state managing could look like this:

1*zxixtZ8lptTc1rrMhXYzqw.png?q=20
jetpack-compose-way-to-animate-android-views-f5ffe0344a0
animated color #1

We will have a single public property called color, and an update callback. Update callback will be called immediately after creating this class to set initial color and later it will be called when animation is running. When new color will be set, AnimatedColor will check if this color is the same, if not it will start an animation.

1*dyOYEcPxjfxDlo9KHkbMwA.png?q=20
jetpack-compose-way-to-animate-android-views-f5ffe0344a0
animated color #2 animation part

Now the animation part. There will be a property called animator which will represent a current animator. And when the new target color value is set and animation triggered, we will

  1. get current color from the animation state
  2. cancel current running animator
  3. create a new animator which will animate previously retrieved color to target
  4. launch new animator
  5. call colorUpdatedCallback with new value retrieved from animation update

This is how we will create this animation from code:

1*gytocx7muMX8hqR0DMcirA.png?q=20
jetpack-compose-way-to-animate-android-views-f5ffe0344a0
usage of AnimatedColor class

As you see using this is pretty easy, you just assign a new target color to color property of AnimatedColor class and it will animate a color automatically. Also notice how AnimatedColor is storing current state of the animation and no additional work required. Here is current animation result:

1*JLFk_flTIfFpc-O72YpY8A.gif?q=20
jetpack-compose-way-to-animate-android-views-f5ffe0344a0
color animation slow speed

I intentionally slowed down the animation speed so we can see that this animation is cancelable and reversible.

1*bBPL_I-T0qaLCnISRUMSeA.gif?q=20
jetpack-compose-way-to-animate-android-views-f5ffe0344a0
color animation normal speed

AnimatedValue

Actually we can abstract more and create more generic class for handling this. Instead of a color we will have a <T> generic value, and instead of creating new ValueAnimator each time we need to run animation, we can reuse existing animator.

1*C6dbjYRWLGv6rJwpTXfX1A.png?q=20
jetpack-compose-way-to-animate-android-views-f5ffe0344a0
AnimatedValue class #1

And actually we don’t need to animate between a color values to animate color. We could just always animate between 0f and 1f with specified time and interpolation, and later just convert somehow this animated float value into <T>. And this “T” could be any type, it could be an Int representing a color, or Int representing a x coordinate of a view, we really don’t care. All we need is to evaluate a float between 0f and 1f into this “T”.

For this purposes Android animation framework provides us with TypeEvaluator<T>. This interface has only one method:

1*JVS52PZhqvELe3PHIiXT9w.png?q=20
jetpack-compose-way-to-animate-android-views-f5ffe0344a0
TypeEvaluator interface

Evaluate method based on current animation fraction (float) and start and end values will return a value T. That’s what we need. And Android framework already provides us with few implementations of this TypeEvaluator. There are an ArgbEvaluator for evaluating colors, FloatEvaluator, IntEvaluator and a few others.

One last thing we need to solve is to store current state of animation. Our AnimatedValue class will have 3 fields:

1*pRn3bPtUFx8OUsyEzjMXDA.png?q=20
jetpack-compose-way-to-animate-android-views-f5ffe0344a0
Values for internal state of animation
  1. valueis needed for updating target state
  2. startValue is needed for storing startValue of a new animation when previous animation canceled
  3. currentValue represents current state of the animation
AnimatedValue<T> full code

How all this are going to work:

1*5-mVlSX27eeP0b6ZX7m4yw.png?q=20
jetpack-compose-way-to-animate-android-views-f5ffe0344a0
AnimatedValue scheme work

For all animating values we could introduce a functions with using own evaluator just for convenience.

1*SEAdtu5iZBtVLD8Zj3T1xw.png?q=20
jetpack-compose-way-to-animate-android-views-f5ffe0344a0
animatedColor function

In terms of animation UI currently nothing changed. We still have our 60 fps smooth cancelable and reversible animation.

1*bBPL_I-T0qaLCnISRUMSeA.gif?q=20
jetpack-compose-way-to-animate-android-views-f5ffe0344a0
color animation

But what if we wanted to add something like animating a change of scale for currently selected tab? How difficult would that be? You are right! Easy-peasy.

1*ANANRFX5-bEcF3sr7w3YCA.png?q=20
jetpack-compose-way-to-animate-android-views-f5ffe0344a0
animating scale between 1f and 1.2f
1*KI_lZgTPyR_nrmozqu53QA.gif?q=20
jetpack-compose-way-to-animate-android-views-f5ffe0344a0
animating scale for selected tab

With only few lines of code we again created cancelable state based animation.

A few other things that can be animated with this:

1*Y_Oyl3QcIdlzDVkeKsZgBA.gif?q=20
jetpack-compose-way-to-animate-android-views-f5ffe0344a0
animating x coordinate of a red ball
1*NbPDQjIyD2YujmF7kyBQTA.gif?q=20
jetpack-compose-way-to-animate-android-views-f5ffe0344a0
animating progress

Hope you enjoyed the reading and will consider to apply this approach into your applications. As always all of the source code is available on GitHub. Cheers!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK