Home Android & Kotlin Books Android Animations by Tutorials

1
Value & Object Animators Written by Prateek Prasad

As smartphones have grown in power and capability over the years, users’ expectations of mobile apps have changed as well. Today’s apps don’t just need to be functional and performant; they also need to offer an elegant UI and a fabulous user experience.

Animations play a crucial part in offering a good user experience for mobile apps. When used correctly and in the proper context, animations add immense value to the user. They can offer subtle visual cues about a task, like indicating a file upload failed or notifying users when the screen’s data changes. You can also leverage animations to transition between different screens, signifying continuity.

Android offers a variety of ways to create animations, and each section of this book focuses on a specific animation category. In this first chapter, you’ll learn about one of the simplest ways of animating views: by using ValueAnimator and ObjectAnimator.

Setting up the project

To start writing your first animation, open the starter project for this chapter in Android Studio.

In this book, you’ll work on a movie review app called Cinematic.

Build and run the project. You’ll get your first glimpse of Cinematic:

The app has three distinct features:

  • Authentication: Mimics the standard signup and login flow.

  • Main feed: A tabbed screen showing you a list of popular movies and movies you’ve added to your list of favorites.

  • Details screen: Tapping a movie takes you to the details screen, which shows you more information including the movie’s synopsis and cast list.

The project is a standard MVVM-based app that uses Modern Android Development best practices.

Expand the project view in Android Studio to look at the package structure for the project:

  • di: Sets up dependency injection for the project using Koin. You can learn more about using this framework with our Dependency Injection With Koin course: https://www.raywenderlich.com/9457-dependency-injection-with-koin.
  • model: Defines all the models that the various screens of the app use.
  • data: Contains the repository setup that the main screen and the details screen use to fetch data about movies. To keep things simple and for consistency, the project uses a prepopulated database for the movie data.
  • util: Contains several utility classes and extensions.
  • favorites: Holds classes related to the favorites screen.
  • popular: Contains classes related to the popular screen.
  • details: Contains classes related to the details screen.

Now that you’ve had a quick walkthrough of the app and the project, it’s time to write your very first animation. :]

Exploring ValueAnimator

Using ValueAnimator is one of the simplest ways to add animations on Android. As the name suggests, ValueAnimator animates a view’s properties — for example, its rotation, scale and alpha — between two specific values over time.

For your first animation, you’ll animate the alpha of the movie poster in MovieDetailsFragment.kt to make it fade in slowly. Open MovieDetailsFragment.kt and create a new function called animatePoster().

private fun animatePoster() {
}

Next, inside animatePoster, set the alpha value of posterContainer to 0, as shown below:

binding.posterContainer.alpha = 0f

This code makes the poster invisible at the start of the animation.

Next, add the following code to set up ValueAnimator:

// 1
val animator = ValueAnimator.ofFloat(0f, 1f)
animator.duration = 1000
// 2
animator.addUpdateListener { valueAnimator ->
  // 3
  val animatedValue = valueAnimator.animatedValue as Float
  // 4  
  binding.posterContainer.alpha = animatedValue       
}
// 5
animator.start()

Here’s a breakdown of what’s going on above:

  1. You instantiated a new ValueAnimator using the static method ofFloat. ofFloat accepts two floating values, and the view’s properties animate between them. Here, you specified a start value of 0 and an end value of 1. The duration specifies how long this animation will run. In this case, it’s 1000, which indicates 1,000 milliseconds or one second.

  2. You added an UpdateListener() to the animator. The animator calls the update listener after every update to the animated value.

  3. Here, you retrieved the current animated value from the animator and cast it to a Float.

  4. You then used the current value to set the poster’s alpha value.

  5. Finally, you started the animation.

Before you can play this animation, you need to replace the TODO item in loadPoster() and call the newly created animatePoster().

animatePoster()

Now, build and run. Tap a movie card to open the details screen. You’ll notice the movie poster slowly fades into view:

Awesome job writing your first animation!

Before you proceed, it’s worth spending time to understand the internals of how this animation — and animations in general! — work. You’ll do that next.

Understanding interpolators

The human eye retains images for a short period, even when the image itself is no longer there. This phenomenon is called persistence of vision. Videos exploit this attribute of our eyes to give us a sense of motion when we’re actually seeing a collection of still images, strategically stitched together.

The images that make up a video are called frames. When an object in the video is changing, this object will be slightly different in each frame.

Animations use a similar premise. First, you define which properties of an object you want to change. Then, you specify the value by which they change and how long it should take for the change to occur. Interpolators control the rate at which each property of a view changes during an animation. In simplified terms, an animation is just a mathematical timing function defined by the rate at which the given property of an object changes.

Android offers a whole suite of interpolators to fit your needs. Based on the one you choose, your animation can range from subtle, to dramatic.

You’ll learn about some of the most common interpolators next.

Linear interpolator

In the linear interpolator, the rate of change of the property’s value stays constant with time. You can use a straight line on a graph to represent this motion. The position of the object changes evenly as time passes.

Position Time

With linear interpolation, animations can end up feeling robotic. In the real world, motion is never linear because friction and other forces influence objects in motion. It’s best to avoid linear interpolators if you want your animations to feel natural.

Accelerate interpolator

In the accelerate interpolator, the rate of change of the property’s value starts slow and accelerates over time.

Position Time

A good example of an accelerated motion would be a car that starts off slow but goes faster once you hit the gas.

Decelerate interpolator

The decelerate interpolator is the opposite of the accelerate interpolator. In the decelerate interpolator, the rate of change of the property’s value starts fast and decelerates over time.

Position Time

Deceleration is quite common in the real world. To visualize the timing curve, think about a ball that you roll on the floor. It starts off fast but slows down over time, eventually coming to a stop.

AccelerateDecelerate interpolator

The AccelerateDecelerate interpolator is a combination of the accelerate and the decelerate interpolator. The rate of change of the property’s value starts slow, speeds up in the middle and ends slow.

Position Time

This is another type of motion pattern that is very common in the real world. Any object that starts off moving fast, slows down and eventually comes to a halt. Think of the motion of a pendulum swing.

Overshoot interpolator

The overshoot interpolator accelerates the property value change, goes beyond the final specified value then, finally, decelerates.

Position Time

Overshoot is one of the more dramatic interpolators in Android. For a good visualization of this kind of motion, think back to any cartoon from your childhood where a leashed dog tried to chase something. The dog goes slightly beyond its leash length and gets pulled back at the end.

Anticipate interpolator

The anticipate interpolator is the opposite of the interpolator. It starts by changing the property’s value to go below the starting value, then accelerates it. One way to use it is to give a dramatic exit effect.

Position Time

The anticipate timing curve is similar to a slingshot — the object is pulled back, and then it shoots off.

Bounce interpolator

In a bounce interpolator, the animation ends in a bounce. Imagine an eraser falling, bouncing slightly when it hits the floor, then settling.

Position Time

Android offers a few more interpolators. You can learn more about them in Android’s interpolator documentation.

Using interpolators

Now that you have a theoretical understanding of interpolators, it’s time to use them in your animations. You’ll start by adding an interpolator to the poster.

In animatePoster(), right after you add the duration for the animation, add the following line:

animator.interpolator = OvershootInterpolator()

Build and run to see the change in how the animation feels. It looks great, doesn’t it?

Note: Interpolators can change the feel of an animation even when its property values stay the same. Play with them to get a sense of what each one brings to the table.

So far, you’ve animated a single property of a view. Wouldn’t it be fun to animate multiple properties at the same time? That’s what you’ll learn to do in the next section.

Animating multiple properties

For your next step, you’ll crank things up a notch and animate another property of the poster. For this next one, alongside the alpha, you’ll also animate the scale of the poster. This animation will make the poster seem to start from nothing, then grow slowly while also becoming increasingly visible.

In animatePoster(), right below where you set the alpha value to posterContainer, add the following code:

binding.posterContainer.scaleX = animatedValue
binding.posterContainer.scaleY = animatedValue

The code you just added will animate the scale of the view over time. It will start at a scale of 0 and gradually scale up to 1.

Build and run. You’ll see the effect in action. Pretty cool, right?

Animating the backdrop

Before you head on to learning about Object Animator, you’ll add one more animation to the details screen.

Create a new function in MovieDetailsFragment.kt named animateBackdrop.

private fun animateBackdrop() {

}

Now, you’ll animate the movie card’s backdrop to subtly make it feel like it’s sliding up from the bottom of the screen.

Add the following code inside animateBackdrop:

//1
val finalYPosition = binding.backdrop.y

//2
val startYPosition = finalYPosition + 40
binding.backdrop.y = startYPosition

//3
val animator = ValueAnimator.ofFloat(startYPosition, finalYPosition)

//4
animator.duration = 1000
animator.interpolator = DecelerateInterpolator()

//5
animator.addUpdateListener { valueAnimator ->
  val animatedValue = valueAnimator.animatedValue as Float
  binding.backdrop.translationY = animatedValue
}
//6
animator.start()

Here’s a breakdown of what’s going on in this function:

  1. You extracted the backdrop ImageView’s current y position and assigned it to a new finalYPosition. Since the backdrop will start slightly below its actual position and animate into its final position, you need to store that final position and assign it to the animator.
  2. You created a new startYPosition to offset the default y position of the backdrop image by 40 pixels and assigned it to the backdrop.
  3. Then, you instantiated a new ValueAnimator using the static method ofFloat and specified the start and end values via the startYPosition and finalYPosition properties.
  4. You assigned 1000 milliseconds for the animation duration and used a DecelerateInterpolator.
  5. Here, you added an updateListener() to the animator. This listener gets the current animatedValue from the animator and casts it to a Float. It then uses the current value to set the backdrop’s y translation value.
  6. Finally, you started the animation.

Call animateBackdrop() from loadBackdrop() by replacing the TODO. Build and run. You’ll see this subtle new addition to the details screen.

ValueAnimator is a powerful and flexible API that allows you to animate a view’s properties reasonably easily. But it can quickly become too verbose when you want to animate multiple views on the screen.

ValueAnimator requires you to:

  • Set up the animator.
  • Specify the terminal values that the animator will work with.
  • Set up updateListener() on the animator.
  • Manipulate the view’s properties based on the updated animation values.
  • Give the animation a duration and start it.

This works well when you want to use a single animator to animate multiple properties of a view in one go, like when you used animatePoster() to animate the scale and the alpha of the poster. When you want to animate only a single property, however, it can get a little tedious. There’s got to be a better way.

Animating single properties with ObjectAnimator

ObjectAnimator is a subclass of ValueAnimator. In cases where you only want to modify a single property of a view, it’s a great choice.

ObjectAnimator abstracts away the responsibility of setting a listener and manually updating the view’s property for the animation. This results in more concise code with the same effect as a ValueAnimator.

Enough talk. Time to see how this works. :]

In MovieDetailsFragment.kt, create a new function called animateText():

  private fun animateText() {}
    //1
    val objectAnimator = ObjectAnimator.ofFloat(
      binding.summary,
      "alpha",
      0f,
      1f
    )

    //2
    objectAnimator.duration = 1000
    objectAnimator.start()
}

Here’s what’s going on:

  1. You instantiated ObjectAnimator using the static function ofFloat(). Unlike ValueAnimator, the object takes two additional arguments: the view you want to animate, which is the summary TextView in this case, and the property of the view you wish to animate. Here, it’s "alpha". Make sure you specify the name correctly, or the animation might not work.
  2. You set the duration for this animation to 1,000 milliseconds — that is, one second — and start the animation.

As a final step, call the newly created animateText() from renderUI() by replacing the TODO item.

animateText()

Build and run. Now, check out this slick and elegant animation that loads the summary of the movie.

Wasn’t that easy? ObjectAnimator is a strong option if you want to spice up a skin’s UX quickly or attract the user’s attention to something important on the screen.

Now that you’ve learned two ways to create animations, you might wonder which properties you can animate. You’ll look at those next.

Animatable properties

The Value Animator API is extremely flexible. Because it gives you a primitive value back as the animated value, you can animate nearly any property of a view. But always remember what Uncle Ben said, “With Great Value Comes Great Responsibility”. You should always ensure that any animations you use add value and context for your users. Janky, unchoreographed animations feel out of place.

Compared to ValueAnimator, however, ObjectAnimator is more limited. You can only animate a subset of a view’s properties. Here are the properties you can animate for a view:

  • alpha: Controls the transparency of a view. 0 is a fully transparent, invisible view and 1 is fully opaque.
  • scaleX and scaleY: Control the size and scale of a view on the X and Y axis, respectively.
  • pivotX and pivotY: Control the view’s pivot point — the point around which the view rotates. By default, the pivot point is at the center of the view.
  • rotation, rotationX, and rotationY: Control the view’s rotation in both 2D and 3D.
  • x and y: Control the location of a view on the X and Y axis, respectively.
  • translationX and translationY: Control the location of a view at an offset from its X and Y axis location, respectively.

This chapter closes off with a few challenges to help you practice your newly acquired animation skills. Be sure to try them out.

Challenges

Challenge 1: Animate the title and rating

Earlier in the chapter, you animated the summary of the movie. To take the UX up a notch, animate the title and rating into view as well.

Your challenge is to figure out which animation option works best for this.

Feel free to try a few different property animation types to see what fits best and looks most consistent with the rest of the animations.

The more you play around with these APIs, the better you’ll understand how animations work on Android.

Challenge 2: Animate the cast image

As of now, the cast section appears directly into view. It would feel consistent if the cast faded into view as well. You could animate the cast list’s entire RecyclerView after assigning the cast information to it, but that would feel odd. Instead, you want to animate the cast image after it loads.

To do it correctly, add a new function to CastViewHolder and call it right after loading the image drawable into the castImage view.

The remaining steps should be relatively straightforward to implement. Pick a property you want to animate and a duration of your choice. Try playing with a few different interpolators to see what feels best in this case. You can also check out the final project to see a possible implementation of it!

In this chapter, you learned the basics of animations and the various options available in Android for animating your views. As you progress through the book, you’ll learn even more sophisticated ways of animating different parts of the app.

Key points

  • Android offers two ways to animate the properties of your views: ValueAnimator and ObjectAnimator.
  • ValueAnimator is a flexible API that lets you take complete control of the animation and animate multiple properties and multiple views at once.
  • ObjectAnimator lets you animate a single property of one view easily.
  • Interpolators let you control the rate of change of the animated property.
  • You can change how an animation feels by playing with the duration and interpolator options.

In the next chapter, you’ll learn all about adding animations to your custom views.

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.

Have feedback to share about the online reading experience? If you have feedback about the UI, UX, highlighting, or other features of our online readers, you can send them to the design team with the form below:

© 2022 Razeware LLC