Chapters

Hide chapters

Android Animations by Tutorials

First Edition · Android 12 · Kotlin 1.5 · Android Studio Artic Fox

Section II: Screen Transitions

Section 2: 3 chapters
Show chapters Hide chapters

7. Basic List Animations
Written by Filip Babić

Heads up... You're reading this book for free, with parts of this chapter shown beyond this point as scrambled text.

It’s hard to show everything your app offers on one static screen. Instead, most developers use a dynamic list of data to display items on demand — making these dynamic lists the most common UI type in mobile apps. Because they’re so common, it’s important to know how to animate dynamic lists.

Animating the items on the list is an opportunity to give your users useful information about what’s happening with that data. For example, you can add animations when items initially appear on the screen or when they’re added, moved or removed. This concept, where you use motion on the screen to give more meaning to your users’ actions, is called meaningful motion. Whenever you add animations to your app, their purpose should always be to give the app more meaningful motion.

In this chapter, you’ll learn about the animations you can apply to your list of movies. More precisely, you’ll learn:

  • How to write simple XML animations, then apply them to list items as layout animations to animate those items when they appear onscreen.
  • What the ItemAnimator API is and how to use it to create, add, remove and move the list items’ animations.
  • How to use the DiffUtil and ListAdapter APIs to emit smart data set changes.

Now, you’ll dive right in by learning about layout animations in lists!

Getting started

To begin, use Android Studio Arctic Fox or newer to open the starter project folder within 07-basic-list-animations in the aat-materials repository. Once the project syncs, build the app. You’ll see the login screen.

Layout animations

Layout animations are basic animations that run whenever you have an XML layout on the screen.

Building layout scale animations

For your first step, you’ll build a scale-up animation for your list items.

<scale
  android:duration="500"
  android:fromXScale="0"
  android:fromYScale="0"
  android:toXScale="1"
  android:toYScale="1" />
<layoutAnimation  xmlns:android="http://schemas.android.com/apk/res/android" 
  android:animation="@anim/scale_item_animation"
  android:animationOrder="normal" />
android:layoutAnimation="@anim/item_animation"

Building translation animations

To build a translation animation, create a new file in the anim folder named vertical_translation_item_animation. Add the following code inside the set tags:

<translate
  android:duration="500"
  android:fromYDelta="-100%"
  android:toYDelta="0%" />
-483% foptuB 4 folifiir guxwi +956% pazyiM

  android:animation="@anim/vertical_translation_item_animation" 

Combining multiple animations

So far, with just a few lines of code, you’ve built two lovely animations for your list items. It’s that easy! But it’s also great to know you can apply multiple animations in your animation set and apply them to each list item. You’ll do that next.

<translate
  android:duration="500"
  android:fromXDelta="-100%"
  android:toXDelta="0%" />

<alpha
  android:duration="500"
  android:fromAlpha="0"
  android:toAlpha="1" />
-354% jikwaG 6 qogibuug jevga +169% qeyguN

android:animation="@anim/combined_item_animation"

Using data set changes to animate list items

To animate list items when the data set changes, you’ll use convenient functions in your RecyclerViews. Open MoviesRecyclerAdapter and take note of notifyDataSetChanged() when you call setItems():

fun setItems(newItems: List<Movie>) {
  this.items.clear()
  this.items.addAll(newItems)
  notifyDataSetChanged() // here
}
ijZefcDueg Hugraf() Edigenid lote mig Azqazoyetuy xoxo Bex mefo yazwuxah nogekdTafoBepSnapsot() avGudzLuirMegjot()

Removing items from the list

Open MoviesRecyclerAdapter.kt. You’ll use this adapter to render popular movies in the PopularMoviesFragment. Once it’s open, navigate to onBindViewHolder().

override fun onBindViewHolder(holder: MoviesViewHolder, position: Int) {
  holder.bind(items[position]) { movie ->
    this.items.remove(movie) // 1

    notifyDataSetChanged() // 2
  }
}
holder.bind(items[position]) { movie ->
  val itemIndex = items.indexOf(movie) // 1
  this.items.remove(movie) // 2
  
  notifyItemRemoved(itemIndex) // 3
}
Upegebib nuka zas Ewgamoxusav wicu Zem caji rekpimak Mudx puk zcivb owol coluchUfukXufupis() Bahfek axidx pilbusxi

Adding items to the list

As with notifyItemRemoved(), you can use notifyItemInserted() to tell the adapter you added new items to the list.

holder.bind(items[position]) { movie ->
  val newIndex = (0..items.size).random() // 1
  this.items.add(newIndex, movie) // 2

  notifyItemInserted(newIndex) // 3
}
Bukt day ubm odar Ixararej leru pof Patyaj uwarb Lxutnig Edbaqelutiq xagu Rev lone nezqolel maxuyvIvorApjinbat()

Using ItemAnimators

Whenever you post any data set changes to the RecyclerView, you trigger its ItemAnimator. Each RecyclerView has an ItemAnimator that you can change programmatically.

myRecyclerView.itemAnimator = object: RecyclerView.ItemAnimator {
    // ... Implement your animations
}

Creating a custom ItemAnimator

Creating custom ItemAnimators isn’t hard. You’ll learn how to do it by building a nice scale-up animation when you add a new item to the list.

class MyItemAnimator : DefaultItemAnimator() { // 1

  override fun animateAdd(holder: RecyclerView.ViewHolder?): Boolean {
    if (holder != null) {
      // 2
      holder.itemView.scaleX = 0f
      holder.itemView.scaleY = 0f
        
      // 3
      holder.itemView.animate()
        .scaleX(1f)
        .scaleY(1f)
        .setDuration(1000)
        .start()
      return true // 4
    }

    return super.animateAdd(holder)
  }
}
binding.popularMoviesList.apply {
  adapter = popularAdapter
  itemAnimator = MyItemAnimator() // apply your animator
}
override fun onBindViewHolder(holder: MoviesViewHolder, position: Int) {
  holder.bind(items[position]) { movie ->
    val newIndex = position + 1 // here
    this.items.add(newIndex, movie)

    notifyItemInserted(newIndex)
  }
}

DiffUtil & ListAdapter

RecyclerView has two APIs that help you update the items in the list automatically: DiffUtil and ListAdapter.

class MoviesDiffCallback : DiffUtil.ItemCallback<Movie>() {
  override fun areItemsTheSame(oldItem: Movie, newItem: Movie): Boolean {
    return oldItem.id == newItem.id
  }

  override fun areContentsTheSame(oldItem: Movie, newItem: Movie): Boolean {
    return oldItem.id == newItem.id
  }
}

Pairing DiffUtil with ListAdapter

DiffUtil isn’t very helpful on its own; it’s often paired up with ListAdapter, another API that automates the way the adapter sends data set changes to the list.

class MoviesAdapter : ListAdapter<Movie, MoviesAdapter.MoviesViewHolder>(MoviesDiffCallback()) { }
private val popularAdapter: MoviesAdapter by inject() // injecting from DI
private fun attachObservers() {
  viewModel.movies.observe(viewLifecycleOwner, { movies ->
    popularAdapter.submitList(movies.shuffled()) // 1

	// 2
  	GlobalScope.launch { 
      repeat(3) { 
        delay(1000)
        popularAdapter.submitList(viewModel.movies.value?.shuffled() ?: emptyList())
      }
    }
  })
  ...
}

DiffUtil internals

Whenever you submit a new list to the adapter, DiffUtil compares all the items in the list based on the conditions in MovieDiffCallback.

Challenge: Add rotation animations

To practice using layout animations, try building a rotation animation using XML. Once you finish, apply it to your RecyclerView and watch your items spin around when they initially appear in the UI.

Key points

  • Using layout animations, you can apply basic animations whenever an item first appears in the list.
  • Layout animations can be translations, scaling, alpha changes and rotation animations.
  • You can combine multiple simple animations within the animation set to define the order they play in.
  • Using data set changes, you can tell your adapters when items are removed, moved or added to the list, or which items changed their contents.
  • There are various data set change functions, so be sure to use the one that best describes your change!
  • To animate data changes, RecyclerView uses ItemAnimator, which exposes many functions to animate different types of changes.
  • If unchanged, RecyclerView uses DefaultItemAnimator, which offers simple, predefined animations.
  • You can create a custom ItemAnimator by extending from DefaultItemAnimator and overriding only the data set functions you want to change.
  • If you don’t want to calculate changes yourself, use the DiffUtil and ListAdapter APIs to implement automatic data changes to your list.
  • When you call submitList() to the adapter, DiffUtil lets it know how to animate items.
  • DiffUtil and ListAdapter use the list’s ItemAnimator to perform required animations.
  • DiffUtil’s computations are optimized, with most only taking 10–30 milliseconds.

Where to go from here?

In the next chapter, you’ll add more options to your list items by adding swipe gestures that let you delete items or mark them as favorites. Once you add features to update the database items, ListAdapter and MovieDiffCallback will ensure you automatically receive updates and more animations in your list.

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.
© 2024 Kodeco Inc.

You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a Kodeco Personal Plan.

Unlock now