Home Android & Kotlin Books Android Animations by Tutorials

8
ItemTouchHelper 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.

You can unlock the rest of this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.

Now that you’ve implemented list and layout animations, you’re ready to upgrade the user experience with gestures, allowing users to swipe items off a list and rearrange them with drag and drop gestures.

In this chapter, you’ll:

  • Enable and recognize gestures in list items.
  • Override swipe gestures to add or remove movies from favorites.
  • Add item resetting to notify users when they’ve swiped an item.
  • Showcase item drag and drop to reorder items.

You’ll achieve all this using the ItemTouchHelper API. Next, you’ll see how.

Getting started

To follow along with this chapter, open the starter project located in 08-itemtouchhelper-animations within the aat-materials repository. This project contains your starting point for this chapter. Here, you’ll add the code to build the final project of this chapter.

Once you open the project, let it sync. Then, build and run. You’ll pick up where you left off in the last chapter.

Your first step toward implementing list gesture animations is to build a callback that will react to the user’s gestures.

Creating ItemTouchHelper.Callback

Before you can implement gesture animations, you need to create an ItemTouchHelper.Callback that will enable and recognize swipe and drag-and-drop gestures.

class MyItemTouchHelperCallback(
  private val moviesRepository: MoviesRepository,
  private val lifecycleOwner: LifecycleOwner
) : ItemTouchHelper.Callback() {}
override fun getMovementFlags(
  recyclerView: RecyclerView,
  viewHolder: RecyclerView.ViewHolder
): Int {
}
  val dragDirectionFlags = ItemTouchHelper.UP or ItemTouchHelper.DOWN
  val swipeDirectionFlags = ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT
}
  return makeMovementFlags(dragDirectionFlags, swipeDirectionFlags) 

Adding item swipe gestures

When the user swipes an item, you’ll either add or remove it from the list of favorites depending on their swipe direction. But first, you need to know which item the user selected. So your next step is to add a way to fetch the Movie item from the swiped ViewHolder.

var movie: Movie? = null
this.movie = movie
inner class MoviesViewHolder(val binding: ItemMovieBinding) :
  RecyclerView.ViewHolder(binding.root) {
    
  var movie: Movie? = null

  fun bind(movie: Movie) {
    this.movie = movie
    
    ...
  }
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
  val movieViewHolder = viewHolder as? MoviesAdapter.MoviesViewHolder
  val movie = movieViewHolder?.movie
}
if (movie != null) { // 1
  val movieId = movie.id // 2
  lifecycleOwner.lifecycleScope.launch { // 3
    if (direction == ItemTouchHelper.RIGHT) { // 4
      moviesRepository.setFavorite(movieId)
    } else if (direction == ItemTouchHelper.LEFT) { // 5
      moviesRepository.removeFavorite(movieId)
    }
  }
}
override fun onMove(
  recyclerView: RecyclerView,
  viewHolder: RecyclerView.ViewHolder,
  target: RecyclerView.ViewHolder
): Boolean {
    return false
}

Connecting the callback to your list

Open PopularMoviesFragment and add the following dependency above onCreateView():

private val moviesRepository: MoviesRepository by inject()
// 1
val itemTouchCallback = MyItemTouchHelperCallback(moviesRepository, viewLifecycleOwner)
  
// 2 
val itemTouchHelper = ItemTouchHelper(itemTouchCallback)
  
// 3
itemTouchHelper.attachToRecyclerView(this)
private val moviesRepository: MoviesRepository by inject()
val itemTouchCallback = MyItemTouchHelperCallback(moviesRepository, viewLifecycleOwner)
val itemTouchHelper = ItemTouchHelper(itemTouchCallback)
itemTouchHelper.attachToRecyclerView(this)

viewHolder.bindingAdapter?.notifyItemChanged(viewHolder.bindingAdapterPosition)

Implementing drag-and-drop gestures

Implementing a simple drag-and-drop gesture isn’t hard. However, MoviesAdapter doesn’t let you make any changes to the position because it’s powered by the database.

Setting up the adapter

First, you’ll switch to using MoviesRecyclerAdapter to implement the gesture. Open PopularMoviesFragment.kt and replace popularAdapter with the following:

private val popularAdapter = MoviesRecyclerAdapter()
inner class MoviesViewHolder(val binding: ItemMovieBinding) :
  RecyclerView.ViewHolder(binding.root) { 
  var movie: Movie? = null

  fun bind(movie: Movie) {
    this.movie = movie
      
    ...
  }
}
fun onItemMoved(oldPosition: Int, newPosition: Int) {
  val itemToReplace = items[oldPosition] // 1
  items.remove(itemToReplace) // 2

  val positionToMove = if (oldPosition > newPosition) newPosition else newPosition - 1 // 3
  items.add(positionToMove, itemToReplace) // 4

  notifyItemMoved(oldPosition, positionToMove) // 5
}
override fun onBindViewHolder(holder: MoviesViewHolder, position: Int) {
  holder.bind(items[position]) { movie ->
    // val newIndex = position + 1
    // this.items.add(newIndex, movie)

    // notifyItemInserted(newIndex)
  }
}

Moving the items

Now that you’ve set up the adapter and the Fragment, head back to ItemTouchHelper.Callback in MyItemTouchHelperCallback.kt. Check out onMove():

  override fun onMove(
    recyclerView: RecyclerView,
    viewHolder: RecyclerView.ViewHolder,
    target: RecyclerView.ViewHolder
  ): Boolean {
    return false
  }
    val adapter = recyclerView.adapter as? MoviesRecyclerAdapter
adapter?.onItemMoved(viewHolder.bindingAdapterPosition, target.bindingAdapterPosition)
return adapter != null

Challenges

Challenge 1: Add a Snackbar notification for swipes

Your first challenge is to improve the experience when swiping items by showing a Snackbar that gives the user more information. Your goal is to implement a notification that tells the user if they added or removed the item from the favorites list.

Challenge 2: Enable right and left directions for drag and drop

Your second challenge is to improve the drag-and-drop gesture experience by adding flags that enable the user to move items in all directions. So instead of supporting just UP and DOWN dragging, you’ll add RIGHT and LEFT drag too.

Key points

  • Swipe animations are great for adding or removing items from lists, showing extra options and showing dialogs.
  • Drag-and-drop animations are useful for reordering items and changing their priorities.
  • ItemTouchHelper is a simple and clean API that lets you enable and react to list item gestures.
  • ItemTouchHelper.Callback gives you more control, while ItemTouchHelper.SimpleCallback offers easier implementation.
  • Using getMovementFlags(), you define which flags the ItemTouchHelper API needs to consume and react to.
  • To build the correct flags, use makeMovementFlags().
  • onSwiped() gives you control over what happens when you swipe items using the helper API. It exposes the swipe direction as well as the ViewHolder that you swiped.
  • To handle drag-and-drop gestures, use onMove().
  • onMove() exposes the parent RecyclerView and the two ViewHolders in question. The ViewHolders represent the item you’ve moved and the position you’re moving the item to.
  • To integrate the ItemTouchHelper.Callback with your RecyclerView, create an ItemTouchHelper with the callback and call itemTouchHelper.attachToRecyclerView(list).
  • When you attach the helper to your list, it automatically propagates the gesture events to your callback.

Where to go from here?

ItemTouchHelper is easy to integrate into lists. It allows you to customize two popular types of motion in lists: swipe and drag and drop. These animations are useful when you change the state of items and data in your app. Depending on the type of app you’re building, you can add many different useful features.

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.

© 2022 Razeware LLC

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 raywenderlich.com Professional subscription.

Unlock Now

To highlight or take notes, you’ll need to own this book in a subscription or purchased by itself.