How to Add Silky Smooth Swipe Animations to CardViews in Your Android App

Animated Cards

Hey there! It‘s Jess, your friendly neighborhood Android dev. If you‘re looking to add some pizazz to your app‘s user experience, you‘ve come to the right place. Today, we‘re going to spice up our CardViews with some slick swipe animations.

But wait, what‘s a CardView you ask? It‘s a snazzy UI element introduced back in Android 5.0 that displays content in – you guessed it – a card format. Cards are a great way to present chunks of information in a clean, modern style that‘s pleasing to the eye.

Basic CardView

While cards are pretty nifty on their own, adding swipe interactions can really make them stand out. Imagine flipping through a deck of cards with a flick of your finger. Pretty satisfying, right?

Swipeable cards open up a world of possibilities for your app‘s interface. You could use them for:

  • Browsing products in a shopping app
  • Swiping through a carousel of images
  • Dismissing items from a to-do list
  • Playing a game of flashcards
  • And so much more!

Sold on the idea? Great! Let‘s roll up our sleeves and get coding. Here‘s what we‘ll be covering:

Steps to Implement Swipe Animations on a CardView

  1. Set up a CardView in your layout file
  2. Detect swipe gestures on the card
  3. Calculate the distance and direction of swipes
  4. Animate the card based on swipe distance
  5. Handle swipe completions and cancellations
  6. Customize your animation‘s look and feel

I‘ve also put together a working example that you can follow along with. You can find the full code on GitHub. Feel free to use it as a starting point for your own project!

Step 1: Creating a CardView

First things first, we need a card to swipe. Pop open your layout file and plop a CardView into a layout of your choice. You can dress it up however you like, but here‘s a basic template to get you started:

<androidx.cardview.widget.CardView
    android:id="@+id/card_view"
    android:layout_width="300dp"
    android:layout_height="400dp"
    app:cardCornerRadius="16dp"
    app:cardElevation="4dp">

    <!-- Add your card content here -->

</androidx.cardview.widget.CardView>

This will give you a nice rounded card that floats above its background. The layout_width and layout_height attributes control the card‘s size, while cardCornerRadius and cardElevation let you customize its appearance.

Diagram of CardView attributes

Within the opening and closing tags, you‘re free to stuff the card with any content your heart desires – text, images, buttons, you name it! I‘ll leave the creative details up to you.

Step 2: Detecting Swipes on the CardView

Now that we have our card ready, it‘s time to teach it some new tricks. To make the card swipeable, we‘ll need to detect touch events using Android‘s handy dandy OnTouchListener.

In your activity or fragment‘s onCreate() method, find the CardView by its ID and set an OnTouchListener on it like so:

val cardView = findViewById<CardView>(R.id.card_view)

cardView.setOnTouchListener { v, event ->
    // We‘ll fill this in next!
    true
}

The OnTouchListener has two parameters: the view that was touched (v) and a MotionEvent describing the touch action. That MotionEvent is going to come in handy for detecting swipes.

There are a few types of motion events we care about: ACTION_DOWN, ACTION_MOVE, and ACTION_UP.

  • ACTION_DOWN fires when the user first touches the screen
  • ACTION_MOVE fires when the user drags their finger across the screen
  • ACTION_UP fires when the user lifts their finger off the screen

To determine if a swipe occurred, we need to track the user‘s finger position over time. We can do that by comparing the X and Y coordinates between an ACTION_DOWN event and subsequent ACTION_MOVE events.

Let‘s define a couple variables to store the initial touch position:

private var initialX = 0f
private var initialY = 0f

cardView.setOnTouchListener { v, event ->
    when (event.action) {
        MotionEvent.ACTION_DOWN -> {
            initialX = event.x
            initialY = event.y
        }
        MotionEvent.ACTION_MOVE -> {
            val deltaX = event.x - initialX
            val deltaY = event.y - initialY
            // TODO: Use the deltas to determine swipe direction
        }
        MotionEvent.ACTION_UP -> {
            // TODO: Handle swipe completion or cancellation  
        }
    }
    true
}

In the ACTION_DOWN branch, we save the initial X and Y coordinates when the user first touches the card. Then, in the ACTION_MOVE branch, we calculate deltaX and deltaY by subtracting the initial coordinates from the current coordinates.

The sign and magnitude of deltaX and deltaY will tell us the direction and distance of the swipe. For example, a large positive deltaX means the user swiped right, while a large negative deltaY means they swiped up.

Step 3: Determining Swipe Direction and Distance

To keep things simple, let‘s focus on supporting horizontal swipes for now. We can classify a swipe as either a "left swipe" or "right swipe" based on the value of deltaX.

private val SWIPE_THRESHOLD = 100 // adjust as needed

// In the ACTION_MOVE branch
val deltaX = event.x - initialX

if (Math.abs(deltaX) > SWIPE_THRESHOLD) {
    if (deltaX > 0) {
        // Right swipe
        // TODO: Start animating card to the right  
    } else {
        // Left swipe  
        // TODO: Start animating card to the left
    }
}

We compare the absolute value of deltaX to a pre-defined threshold to avoid triggering swipes for small, unintentional movements. Feel free to tweak the threshold to get the sensitivity just right for your app.

Step 4: Animating the CardView

The real magic happens when we start animating the card in response to swipes. The basic idea is to update the card‘s X position on every ACTION_MOVE event based on the swipe distance. We can do this with a View animation.

// In the ACTION_MOVE branch
if (deltaX > 0) {
    // Right swipe
    card.animate()
        .translationX(deltaX)
        .setDuration(0)
        .start()
} else {
    // Left swipe
    card.animate() 
        .translationX(deltaX)
        .setDuration(0)
        .start()
}

Calling animate() on the card gives us a ViewPropertyAnimator to work with. We use translationX() to set the card‘s new X position and setDuration(0) to make the change instantaneous. Finally, start() sets the whole thing in motion.

With this code, the card will follow the user‘s finger as they swipe left or right. Give it a whirl and watch your card slide around the screen!

We can jazz this up even more by adding some extra visual polish. For starters, let‘s add a subtle fade out as the card reaches the edge of the screen.

val screenWidth = Resources.getSystem().displayMetrics.widthPixels

card.animate()
    .translationX(deltaX)
    .alpha(Math.max(0f, 1f - 2f * Math.abs(deltaX) / screenWidth))
    .setDuration(0)
    .start()

Using the alpha() method, we can adjust the card‘s opacity based on how far it‘s been swiped. I like to ramp down the opacity from 1 to 0 as the swipe distance approaches half the screen width, but feel free to play with the numbers to get an effect you like.

Card fading out during swipe

Step 5: Handling Swipe Results

The final piece of the puzzle is deciding what to do when a swipe is completed. We need a way to detect when the user releases the card after swiping it far enough to trigger an action.

That‘s where the ACTION_UP event comes in. When the user lifts their finger, we check deltaX one last time to see if the swipe distance exceeds a certain threshold.

private val SWIPE_THRESHOLD = 800 // adjust as needed
private val SWIPE_VELOCITY_THRESHOLD = 400 // adjust as needed

// In the ACTION_UP branch 
if (Math.abs(deltaX) > SWIPE_THRESHOLD) {
    val endX = if (deltaX > 0) screenWidth else -screenWidth

    card.animate()
        .translationX(endX.toFloat())
        .alpha(0f)
        .setDuration(200)
        .setListener(object : Animator.AnimatorListener {
            override fun onAnimationEnd(animation: Animator) {
                // Swipe completed, do something with the swiped card
                card.visibility = View.GONE
                onCardSwiped()
            }

            override fun onAnimationStart(animation: Animator) {}
            override fun onAnimationCancel(animation: Animator) {}
            override fun onAnimationRepeat(animation: Animator) {}
        })
        .start()

} else {
    // Not a swipe, restore the card to its original position
    card.animate()
        .translationX(0f)
        .alpha(1f)
        .setDuration(200)
        .start()
}

If the swipe distance exceeds the threshold, we animate the card off the edge of the screen and fade it out completely. The setListener() method lets us define a callback to run when the animation finishes – in this case, we hide the card view and trigger our own onCardSwiped() method to handle the swipe action.

If the swipe distance doesn‘t meet the threshold, we instead animate the card back to its original position and restore its opacity.

Congratulations, you‘ve just implemented swipe-to-dismiss on a CardView! Feels good, doesn‘t it? The possibilities are endless – you could swipe to delete, swipe to save, or even chain multiple swipe actions together.

Step 6: Optimizing and Customizing

While you‘ve got a solid foundation to build on, there‘s always room to optimize and customize. Here are some ideas to take your swipe animations to the next level:

  • Experiment with animation timing and interpolation for a smoother, more polished feel
  • Add visual indicators like "delete" or "save" icons that appear during swipes
  • Support multiple swipe directions – left, right, up, down
  • Dynamically adjust swipe thresholds based on screen size
  • Create a reusable SwipeableCardView class to keep your code DRY
  • Handle edge cases gracefully – what if the user swipes and then quickly reverses direction?

The key to buttery swipe animations is striking a balance between responsiveness and smoothness. Play around with velocity tracking, animation curves, and touch slop to find a combination that works for your app.

Go Forth and Swipe

Phew, you still with me? We covered a lot of ground! I hope you‘re feeling inspired to add some swiping goodness to your own app. Trust me, your users will thank you.

Remember, animations are a powerful tool for enhancing your app‘s UX. When used judiciously, they can make your interface feel more intuitive, responsive, and fun. Just be sure not to overdo it – no one wants to use an app that feels like a Vegas casino.

If you‘re thirsting for more Android animation knowledge, check out the official docs on Animation and Graphics. There‘s a wealth of information on view animations, property animations, transitions, and more.

You can also find tons of open source libraries on GitHub that make complex animations a breeze. A few of my favorites are RecyclerView Animators, Lottie, and Material Animations. Stand on the shoulders of giants, I always say.

That‘s all from me, folks. Now go forth and make something amazing! If you have any questions or just want to show off your slick swipe animations, hit me up on Twitter – @JessicaDubya. Until next time, keep coding and stay swipey!

Similar Posts