Open In App

Tinder Swipe View with Example in Android

Last Updated : 18 Apr, 2025
Comments
Improve
Suggest changes
Like Article
Like
Report

Tinder Swipe View is one of the most used UI components in many Android apps. This feature provides us to easily represent the data in a huge list form. In this article, we will take a look at implementing this Swipe View feature in our Android App. 

What we are going to build in this Article? 

We will be building a simple application in which we will be displaying a stack of cards in which we will be displaying the different courses which are available on Geeks for Geeks. On that cards, we will be adding a swipe-like feature similar to that of Tinder. A sample GIF is given below to get an idea about what we are going to do in this article. Note that we are going to implement this project using Java/Kotlin language. 

swipee

swipe left/right


Step by Step Implementation

Step 1: Create a New Project

To create a new project in Android Studio please refer to How to Create/Start a New Project in Android Studio.

Step 2: Add below dependency in build.gradle.kts (Module :app)

Navigate to the app > Gradle Scripts > build.gradle.kts (Module :app) and add the below dependency in the dependencies section. 

dependencies {
...
implementation ("com.github.yuyakaido:CardStackView:v2.3.4")
implementation ("com.github.bumptech.glide:glide:4.13.2")
}

Navigate to the app > Gradle Scripts > settings.gradle.kts and add the below line of code in the repositories section. 

dependencyResolutionManagement {
...
repositories {
...
maven { setUrl("https://jitpack.io") }
}
}

After adding the above dependency now sync your project and we will move towards our activity_main.xml. 


Step 3: Working with activity_main.xml file

Navigate to the app > res > layout > activity_main.xml and add the below code to that file. Create a new layout for each item of the CardStackView and name it item_spot.xml. Create a new drawable with the name gradient_bg.xml which will be used as a gradient in front of the image.

activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/white"
    android:orientation="vertical">

    <com.yuyakaido.android.cardstackview.CardStackView
        android:id="@+id/card_stack_view"
        android:layout_width="match_parent"
        android:layout_height="400dp"
        android:padding="4dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
item_spot.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="?attr/selectableItemBackground"
    android:foreground="?attr/selectableItemBackground"
    app:cardUseCompatPadding="true"
    app:cardPreventCornerOverlap="false"
    app:cardCornerRadius="8dp"
    app:cardBackgroundColor="@android:color/white">

    <ImageView
        android:id="@+id/item_image"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scaleType="centerCrop" />

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:padding="16dp"
        android:background="@drawable/gradient_bg">

        <TextView
            android:id="@+id/item_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textStyle="bold"
            android:textColor="@android:color/white"
            android:textSize="26sp"/>

        <TextView
            android:id="@+id/item_city"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textStyle="bold"
            android:textColor="@android:color/white"
            android:textSize="20sp"/>

    </LinearLayout>

    <FrameLayout
        android:id="@+id/top_overlay"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </FrameLayout>

    <FrameLayout
        android:id="@+id/bottom_overlay"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </FrameLayout>

</androidx.cardview.widget.CardView>
gradient_bg.xml
<?xml version="1.0" encoding="utf-8"?>
<shape 
    xmlns:android="http://schemas.android.com/apk/res/android">
    <gradient
        android:startColor="@android:color/transparent"
        android:endColor="#88000000"
        android:angle="270" />
        
    <corners
        android:bottomLeftRadius="8dp"
        android:bottomRightRadius="8dp" />
</shape>


Step 4: Creating a modal class for storing our data

Navigate to the app > java > {package-name}, Right-click on it, New > Java/Kotlin class and name it as Spot.

Spot.java
package org.geeksforgeeks.demo;

// A simple class to hold information about a spot
public class Spot {
    // Auto-incremented unique ID
    private final long id;
    // Name of the spot
    private final String name;
    // City where the spot is located
    private final String city;
    // Related URL
    private final String url;

    // Static counter to generate unique IDs
    private static long counter = 0;

    // Constructor
    public Spot(String name, String city, String url) {
        this.id = counter++;
        this.name = name;
        this.city = city;
        this.url = url;
    }

    // Getters
    public long getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public String getCity() {
        return city;
    }

    public String getUrl() {
        return url;
    }
}
Spot.kt
package org.geeksforgeeks.demo

// A simple data class to hold information about a spot
data class Spot(
    // Auto-incremented unique ID
    val id: Long = counter++,
    // Name of the spot
    val name: String,
    // City where the spot is located
    val city: String,
    // Related URL
    val url: String
) {
    companion object {
        // Counter to generate unique IDs
        private var counter = 0L
    }
}


Step 5: Creating an adapter class

Now for setting data to each card present in the stack, we have to create our adapter class. Navigate to the app > java > {package-name}, Right-click on it, New > Java/Kotlin class and name it as CardStackAdapter and add the below code to it.

CardStackAdapter.java
package org.geeksforgeeks.demo;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.recyclerview.widget.RecyclerView;

import com.bumptech.glide.Glide;

import java.util.List;

// Adapter for binding Spot data to RecyclerView cards
public class CardStackAdapter extends RecyclerView.Adapter<CardStackAdapter.ViewHolder> {

    private List<Spot> spots;

    public CardStackAdapter(List<Spot> spots) {
        this.spots = spots;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.item_spot, parent, false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        Spot spot = spots.get(position);
        holder.name.setText(spot.getName());
        holder.city.setText(spot.getCity());

        Glide.with(holder.image.getContext())
                .load(spot.getUrl())
                .into(holder.image);
    }

    @Override
    public int getItemCount() {
        return spots != null ? spots.size() : 0;
    }

    // ViewHolder class to hold references to views
    public static class ViewHolder extends RecyclerView.ViewHolder {
        public TextView name;
        public TextView city;
        public ImageView image;

        public ViewHolder(View view) {
            super(view);
            name = view.findViewById(R.id.item_name);
            city = view.findViewById(R.id.item_city);
            image = view.findViewById(R.id.item_image);
        }
    }
}
CardStackAdapter.kt
package org.geeksforgeeks.demo

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide

// Adapter for binding Spot data to RecyclerView cards
class CardStackAdapter(
    private var spots: List<Spot> = emptyList()
) : RecyclerView.Adapter<CardStackAdapter.ViewHolder>() {

    // Creates a new ViewHolder when needed
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val inflater = LayoutInflater.from(parent.context)
        return ViewHolder(inflater.inflate(R.layout.item_spot, parent, false))
    }

    // Binds data to the ViewHolder
    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val spot = spots[position]
        holder.name.text = spot.name
        holder.city.text = spot.city

        Glide.with(holder.image)
            .load(spot.url)
            .into(holder.image)
    }

    // Returns the total number of items
    override fun getItemCount(): Int {
        return spots.size
    }

    // Holds references to the views in each item
    class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        val name: TextView = view.findViewById(R.id.item_name)
        var city: TextView = view.findViewById(R.id.item_city)
        var image: ImageView = view.findViewById(R.id.item_image)
    }
}


Step 6: Working with the MainActivity file

Go to the MainActivity file and refer to the following code. Below is the code for the MainActivity file. Comments are added inside the code to understand the code in more detail.

MainActivity.java
package org.geeksforgeeks.demo;

import android.os.Bundle;
import android.view.View;
import android.view.animation.LinearInterpolator;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.DefaultItemAnimator;

import com.yuyakaido.android.cardstackview.*;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity implements CardStackListener {

    private CardStackView cardStackView;
    private CardStackLayoutManager manager;
    private CardStackAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        cardStackView = findViewById(R.id.card_stack_view);
        manager = new CardStackLayoutManager(this, this);
        adapter = new CardStackAdapter(createSpots());
        setupCardStackView();
    }

    private void setupCardStackView() {
        initialize();
    }

    private void initialize() {
        manager.setStackFrom(StackFrom.None);
        manager.setVisibleCount(3);
        manager.setTranslationInterval(8.0f);
        manager.setScaleInterval(0.95f);
        manager.setSwipeThreshold(0.3f);
        manager.setMaxDegree(20.0f);
        manager.setDirections(Direction.HORIZONTAL);
        manager.setCanScrollHorizontal(true);
        manager.setCanScrollVertical(true);
        manager.setSwipeableMethod(SwipeableMethod.AutomaticAndManual);
        manager.setOverlayInterpolator(new LinearInterpolator());

        cardStackView.setLayoutManager(manager);
        cardStackView.setAdapter(adapter);

        if (cardStackView.getItemAnimator() instanceof DefaultItemAnimator) {
            ((DefaultItemAnimator) cardStackView.getItemAnimator()).setSupportsChangeAnimations(false);
        }
    }

    private List<Spot> createSpots() {
        List<Spot> spots = new ArrayList<>();
        spots.add(new Spot("Example", "Kyoto", "https://images.pexels.com/photos/8361426/pexels-photo-8361426.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1"));
        spots.add(new Spot("Example", "Kyoto", "https://images.pexels.com/photos/18301868/pexels-photo-18301868/free-photo-of-traveler-in-a-headscarf-with-a-backpack-on-the-subway-platform.jpeg?auto=compress&cs=tinysrgb&w=600&lazy=load"));
        spots.add(new Spot("Example", "Kyoto", "https://images.pexels.com/photos/30652095/pexels-photo-30652095/free-photo-of-elegant-woman-in-white-dress-outdoors.jpeg?auto=compress&cs=tinysrgb&w=600&lazy=load"));
        spots.add(new Spot("Example", "New York", "https://images.pexels.com/photos/30800337/pexels-photo-30800337/free-photo-of-playful-border-collie-enjoying-snowy-winter-day.jpeg?auto=compress&cs=tinysrgb&w=600&lazy=load"));
        spots.add(new Spot("Example", "New York", "https://images.pexels.com/photos/31102315/pexels-photo-31102315/free-photo-of-casa-batllo-architectural-detail-in-barcelona.jpeg?auto=compress&cs=tinysrgb&w=600&lazy=load"));
        spots.add(new Spot("Example", "New York", "https://images.pexels.com/photos/31049335/pexels-photo-31049335/free-photo-of-majestic-longhorn-cow-grazing-in-wisconsin-field.jpeg?auto=compress&cs=tinysrgb&w=600&lazy=load"));
        return spots;
    }

    @Override
    public void onCardDragging(Direction direction, float ratio) {
        // Optional: implement if needed
    }

    @Override
    public void onCardSwiped(Direction direction) {
        if (direction == Direction.Right) {
            Toast.makeText(this, "Swiped right", Toast.LENGTH_SHORT).show();
        } else if (direction == Direction.Left) {
            Toast.makeText(this, "Swiped left", Toast.LENGTH_SHORT).show();
        }
    }

    @Override
    public void onCardRewound() {
        // Optional: implement if needed
    }

    @Override
    public void onCardCanceled() {
        // Optional: implement if needed
    }

    @Override
    public void onCardAppeared(View view, int position) {
        // Optional: implement if needed
    }

    @Override
    public void onCardDisappeared(View view, int position) {
        // Optional: implement if needed
    }
}
MainActivity.kt
package org.geeksforgeeks.demo

import android.os.Bundle
import android.view.View
import android.view.animation.LinearInterpolator
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.DefaultItemAnimator
import com.yuyakaido.android.cardstackview.*

class MainActivity : AppCompatActivity(), CardStackListener {

    // Card stack view and related components
    private lateinit var cardStackView: CardStackView
    private lateinit var manager: CardStackLayoutManager
    private lateinit var adapter: CardStackAdapter

    // Called when the activity is created
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        cardStackView = findViewById(R.id.card_stack_view)
        manager = CardStackLayoutManager(this, this)
        adapter = CardStackAdapter(createSpots())
        setupCardStackView()
    }

    // Called when a card is being dragged
    override fun onCardDragging(direction: Direction, ratio: Float) {}

    // Called when a card is swiped
    override fun onCardSwiped(direction: Direction) {
        if (direction == Direction.Right) {
            Toast.makeText(this, "Swiped right", Toast.LENGTH_SHORT).show()
        } else if (direction == Direction.Left) {
            Toast.makeText(this, "Swiped left", Toast.LENGTH_SHORT).show()
        }
    }

    // Called when a card is rewound
    override fun onCardRewound() {}

    // Called when a swipe is canceled
    override fun onCardCanceled() {}

    // Called when a card appears on screen
    override fun onCardAppeared(view: View, position: Int) {}

    // Called when a card disappears from screen
    override fun onCardDisappeared(view: View, position: Int) {}

    // Sets up the card stack view
    private fun setupCardStackView() {
        initialize()
    }

    // Initializes the card stack manager and adapter
    private fun initialize() {
        manager.setStackFrom(StackFrom.None)
        manager.setVisibleCount(3)
        manager.setTranslationInterval(8.0f)
        manager.setScaleInterval(0.95f)
        manager.setSwipeThreshold(0.3f)
        manager.setMaxDegree(20.0f)
        manager.setDirections(Direction.HORIZONTAL)
        manager.setCanScrollHorizontal(true)
        manager.setCanScrollVertical(true)
        manager.setSwipeableMethod(SwipeableMethod.AutomaticAndManual)
        manager.setOverlayInterpolator(LinearInterpolator())

        cardStackView.layoutManager = manager
        cardStackView.adapter = adapter

        cardStackView.itemAnimator.apply {
            if (this is DefaultItemAnimator) {
                supportsChangeAnimations = false
            }
        }
    }

    // Creates a list of Spot objects to show in the cards
    private fun createSpots(): List<Spot> {
        val spots = ArrayList<Spot>()
        spots.add(Spot(name = "Example", city = "Kyoto", url = "https://images.pexels.com/photos/8361426/pexels-photo-8361426.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=1"))
        spots.add(Spot(name = "Example", city = "Kyoto", url = "https://images.pexels.com/photos/18301868/pexels-photo-18301868/free-photo-of-traveler-in-a-headscarf-with-a-backpack-on-the-subway-platform.jpeg?auto=compress&cs=tinysrgb&w=600&lazy=load"))
        spots.add(Spot(name = "Example", city = "Kyoto", url = "https://images.pexels.com/photos/30652095/pexels-photo-30652095/free-photo-of-elegant-woman-in-white-dress-outdoors.jpeg?auto=compress&cs=tinysrgb&w=600&lazy=load"))
        spots.add(Spot(name = "Example", city = "New York", url = "https://images.pexels.com/photos/30800337/pexels-photo-30800337/free-photo-of-playful-border-collie-enjoying-snowy-winter-day.jpeg?auto=compress&cs=tinysrgb&w=600&lazy=load"))
        spots.add(Spot(name = "Example", city = "New York", url = "https://images.pexels.com/photos/31102315/pexels-photo-31102315/free-photo-of-casa-batllo-architectural-detail-in-barcelona.jpeg?auto=compress&cs=tinysrgb&w=600&lazy=load"))
        spots.add(Spot(name = "Example", city = "New York", url = "https://images.pexels.com/photos/31049335/pexels-photo-31049335/free-photo-of-majestic-longhorn-cow-grazing-in-wisconsin-field.jpeg?auto=compress&cs=tinysrgb&w=600&lazy=load"))
        return spots
    }
}


Output:




Next Article

Similar Reads