In my recent article Gmail like Inbox we have successfully cloned the gmail’s inbox using a RecyclerView with some cool animations. But one thing missing in that is adding swipe to delete and undo functionalities as gmail does.

In this article we are going to add swipe to delete and undo options in a recycler view. Instead of continuing that article, we are going to start a fresh project to make it simpler to understand.

1. How RecyclerView Swipe works? ItemTouchHelper

With the help of ItemTouchHelper class you can add swipe to dismiss, drag & drop support to RecyclerView. Swiping the row will remove the row from the RecyclerView, but it won’t refresh the data. You can see an empty row displayed on swiping the row. You have to take care of refreshing the list by removing the item from the adapter dataset.

ItemTouchHelper.SimpleCallback provides certain callback methods like onMove(), onChildDraw(), onSwiped() when the row is swiped. Showing the background view, removing the item from adapter can be done using these callback methods.

Below is the code snipped of ItemTouchHelper and attaching it to recycler view. This code has to be bit modified in order to make the swipe and undo works. We’ll see how to do that shortly.

RecyclerView recyclerView = findViewById(R.id.recycler_view);

ItemTouchHelper.SimpleCallback itemTouchHelperCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT) {
    @Override
        public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
        return false;
    }

    @Override
    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
        // Row is swiped from recycler view
        // remove it from adapter
    }

    @Override
        public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
            // view the background view
        }
    };
        
// attaching the touch helper to recycler view
new ItemTouchHelper(itemTouchHelperCallback).attachToRecyclerView(recyclerView);

1.1 Defining the Swipe Directions

The swipe directions can be decided while creating the SimpleCallback(). In this article we just used Left -> Right direction. If you want other directions, you can combine with | operator. We can define LEFT, RIGHT, UP and DOWN directions.

new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT)

1.2 Designing The Layout

Planning the layout is very important while adding background view of the row. Lot of people are providing the background by drawing the view on a Canvas in onChildDraw() method. This will be a tedious process drawing it on a canvas if the background view complex. Design the same in an xml layout is much easier. So I have placed both the foreground and background views in a simple layout using FrameLayout keeping the foreground layout on top.

The foreground view will be always visible in the recycler view, and when swipe is performed the background will be visible staying in a static position.

Below image will give you an idea how the foreground and background views are placed in a frame.

2. Example JSON – Restaurant Menu

I have created an example JSON that contains restaurant menu with proper names and images. We are going to consume this json to display the food items in our RecyclerView list.

https://api.androidhive.info/json/menu.json

3. Creating New Project

Now that we have the required knowledge of ItemTouchHelper, let’s start a new project and see how to get the desired output we are looking for.

Note: This project is developed using Android Studio 3.0 Beta. In order to get this article working, get the latest the version of Android Studio here.

1. Create a new project in Android Studio from File ⇒ New Project and select Basic Activity from templates.

2. Open build.gradle located under app folder and add RecyclerView, Glide and Volley dependencies.

dependencies {
    implementation 'com.android.support:recyclerview-v7:26.1.0'
 
    // glide image library
    implementation 'com.github.bumptech.glide:glide:3.7.0'
    
    // volley http library
    implementation 'com.android.volley:volley:1.0.0'
    implementation 'com.google.code.gson:gson:2.6.2'
}

3. Add the below resources to your strings.xml, dimens.xml and colors.xml respectively.

<resources>
    <string name="app_name">Recycler Swipe</string>
    <string name="action_settings">Settings</string>
    <string name="my_cart">My Cart</string>
    <string name="delete">DELETE</string>
</resources>
<resources>
    <dimen name="activity_padding_horizontal">16dp</dimen>
    <dimen name="padd_10">10dp</dimen>
    <dimen name="ic_delete">30dp</dimen>
    <dimen name="thumbnail">90dp</dimen>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#111</color>
    <color name="colorPrimaryDark">#FFF</color>
    <color name="colorAccent">#ea3732</color>
    <color name="bg_row_background">#fa315b</color>
    <color name="item_name">#535353</color>
    <color name="description">#a9a9a9</color>
</resources>

4. Open styles.xml add the below styles to apply Lighter theme to your app. This make the text to darker as we have white background to toolbar.

<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

    <style name="AppTheme.NoActionBar">
        <item name="windowActionBar">false</item>
        <item name="windowNoTitle">true</item>
    </style>

    <style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Light" />

    <style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />

</resources>

5. Create a class named MyApplication.java and extend it from Application. This is a singleton class used to initiate the volley library.

import android.app.Application;
import android.text.TextUtils;

import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.toolbox.Volley;

public class MyApplication extends Application {

    public static final String TAG = MyApplication.class
            .getSimpleName();

    private RequestQueue mRequestQueue;

    private static MyApplication mInstance;

    @Override
    public void onCreate() {
        super.onCreate();
        mInstance = this;
    }

    public static synchronized MyApplication getInstance() {
        return mInstance;
    }

    public RequestQueue getRequestQueue() {
        if (mRequestQueue == null) {
            mRequestQueue = Volley.newRequestQueue(getApplicationContext());
        }

        return mRequestQueue;
    }

    public <T> void addToRequestQueue(Request<T> req, String tag) {
        // set the default tag if tag is empty
        req.setTag(TextUtils.isEmpty(tag) ? TAG : tag);
        getRequestQueue().add(req);
    }

    public <T> void addToRequestQueue(Request<T> req) {
        req.setTag(TAG);
        getRequestQueue().add(req);
    }

    public void cancelPendingRequests(Object tag) {
        if (mRequestQueue != null) {
            mRequestQueue.cancelAll(tag);
        }
    }
}

6. Open AndroidManifest.xml and add MyApplication class to <application> node. Also add the INTERNET permission as we gonna make http calls.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="info.androidhive.recyclerviewswipe">

    <uses-permission android:name="android.permission.INTERNET" />

    <application
        android:name=".MyApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name"
            android:theme="@style/AppTheme.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

Now we have all the resources ready. Let’s start adding the RecyclerView and rendering the data by parsing the json.

7. Open layout file your MainActivity.java and add the RecyclerView. For my activity I have two layout files, activity_main.xml and content_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout 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:id="@+id/coordinator_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="info.androidhive.recyclerviewswipe.MainActivity">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="@android:color/white"
            app:popupTheme="@style/AppTheme.PopupOverlay" />

    </android.support.design.widget.AppBarLayout>

    <include layout="@layout/content_main" />

</android.support.design.widget.CoordinatorLayout>
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="info.androidhive.recyclerviewswipe.MainActivity"
    tools:showIn="@layout/activity_main">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:scrollbars="vertical" />
    
</android.support.constraint.ConstraintLayout>

8. Create a class named Item.class and add the below. This POJO class contains menu item name, price, description and url of thumbnail image.

/**
 * Created by ravi on 26/09/17.
 */

public class Item {
    int id;
    String name;
    String description;
    double price;
    String thumbnail;

    public Item() {
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public String getThumbnail() {
        return thumbnail;
    }

    public void setThumbnail(String thumbnail) {
        this.thumbnail = thumbnail;
    }
}

9. Create an xml layout named cart_list_item.xml under res ⇒ layout folder. This layout file renders each row in recycler view using the adapter class. Here we are adding an ImageView for thumbnail and few TextViews to display the menu item name, description and price.

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <RelativeLayout
        android:id="@+id/view_background"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/bg_row_background">

        <ImageView
            android:id="@+id/delete_icon"
            android:layout_width="@dimen/ic_delete"
            android:layout_height="@dimen/ic_delete"
            android:layout_alignParentRight="true"
            android:layout_centerVertical="true"
            android:layout_marginRight="@dimen/padd_10"
            android:src="@drawable/ic_delete_white_24dp" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_marginRight="@dimen/padd_10"
            android:layout_toLeftOf="@id/delete_icon"
            android:text="@string/delete"
            android:textColor="#fff"
            android:textSize="13dp" />

    </RelativeLayout>

    <RelativeLayout
        android:id="@+id/view_foreground"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@android:color/white"
        android:padding="@dimen/padd_10">

        <ImageView
            android:id="@+id/thumbnail"
            android:layout_width="@dimen/thumbnail"
            android:layout_height="@dimen/thumbnail"
            android:layout_marginRight="@dimen/activity_padding_horizontal"
            android:scaleType="centerCrop" />

        <TextView
            android:id="@+id/name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_toRightOf="@id/thumbnail"
            android:ellipsize="end"
            android:fontFamily="sans-serif-medium"
            android:maxLines="1"
            android:textColor="@color/item_name"
            android:textSize="17dp" />

        <TextView
            android:id="@+id/description"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@id/name"
            android:layout_marginTop="5dp"
            android:layout_toRightOf="@id/thumbnail"
            android:textColor="@color/description"
            android:textSize="12dp" />

        <TextView
            android:id="@+id/price"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentBottom="true"
            android:layout_toRightOf="@id/thumbnail"
            android:textColor="@color/colorAccent"
            android:textStyle="bold" />

    </RelativeLayout>
</FrameLayout>

10. Create a class named CartListAdapter.java. This adapter class will be used to inflate the layouts with proper data in recycler view. I am also adding two additional methods to this class removeItem() and restoreItem(), to remove / add rows to recycler view.

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;

import com.bumptech.glide.Glide;

import java.util.List;

public class CartListAdapter extends RecyclerView.Adapter<CartListAdapter.MyViewHolder> {
    private Context context;
    private List<Item> cartList;

    public class MyViewHolder extends RecyclerView.ViewHolder {
        public TextView name, description, price;
        public ImageView thumbnail;
        public RelativeLayout viewBackground, viewForeground;

        public MyViewHolder(View view) {
            super(view);
            name = view.findViewById(R.id.name);
            description = view.findViewById(R.id.description);
            price = view.findViewById(R.id.price);
            thumbnail = view.findViewById(R.id.thumbnail);
            viewBackground = view.findViewById(R.id.view_background);
            viewForeground = view.findViewById(R.id.view_foreground);
        }
    }


    public CartListAdapter(Context context, List<Item> cartList) {
        this.context = context;
        this.cartList = cartList;
    }

    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View itemView = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.cart_list_item, parent, false);

        return new MyViewHolder(itemView);
    }

    @Override
    public void onBindViewHolder(MyViewHolder holder, final int position) {
        final Item item = cartList.get(position);
        holder.name.setText(item.getName());
        holder.description.setText(item.getDescription());
        holder.price.setText("₹" + item.getPrice());

        Glide.with(context)
                .load(item.getThumbnail())
                .into(holder.thumbnail);
    }

    @Override
    public int getItemCount() {
        return cartList.size();
    }

    public void removeItem(int position) {
        cartList.remove(position);
        // notify the item removed by position
        // to perform recycler view delete animations
        // NOTE: don't call notifyDataSetChanged()
        notifyItemRemoved(position);
    }

    public void restoreItem(Item item, int position) {
        cartList.add(position, item);
        // notify item added by position
        notifyItemInserted(position);
    }
}

11. Now we are at the core part of this article i.e adding swipe functionality to recycler view. To achieve this create a class named RecyclerItemTouchHelper.java and and extend the class from ItemTouchHelper.SimpleCallback and override the necessary methods.

> getDefaultUIUtil() will be used by ItemTouchHelper to detect whenever there is UI change on the view. We use this function to keep the background view in a static position and move the foreground view.

> In onChildDrawOver() the x-position of the foreground view is changed while user is swiping the view.

> RecyclerItemTouchHelperListener interface used to send the callback to implementing activity. Here the listener will be triggered in MainActivity once the swipe is done.

import android.graphics.Canvas;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
import android.view.View;

/**
 * Created by ravi on 29/09/17.
 */

public class RecyclerItemTouchHelper extends ItemTouchHelper.SimpleCallback {
    private RecyclerItemTouchHelperListener listener;

    public RecyclerItemTouchHelper(int dragDirs, int swipeDirs, RecyclerItemTouchHelperListener listener) {
        super(dragDirs, swipeDirs);
        this.listener = listener;
    }

    @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
        return true;
    }

    @Override
    public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
        if (viewHolder != null) {
            final View foregroundView = ((CartListAdapter.MyViewHolder) viewHolder).viewForeground;

            getDefaultUIUtil().onSelected(foregroundView);
        }
    }

    @Override
    public void onChildDrawOver(Canvas c, RecyclerView recyclerView,
                                RecyclerView.ViewHolder viewHolder, float dX, float dY,
                                int actionState, boolean isCurrentlyActive) {
        final View foregroundView = ((CartListAdapter.MyViewHolder) viewHolder).viewForeground;
        getDefaultUIUtil().onDrawOver(c, recyclerView, foregroundView, dX, dY,
                actionState, isCurrentlyActive);
    }

    @Override
    public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        final View foregroundView = ((CartListAdapter.MyViewHolder) viewHolder).viewForeground;
        getDefaultUIUtil().clearView(foregroundView);
    }

    @Override
    public void onChildDraw(Canvas c, RecyclerView recyclerView,
                            RecyclerView.ViewHolder viewHolder, float dX, float dY,
                            int actionState, boolean isCurrentlyActive) {
        final View foregroundView = ((CartListAdapter.MyViewHolder) viewHolder).viewForeground;

        getDefaultUIUtil().onDraw(c, recyclerView, foregroundView, dX, dY,
                actionState, isCurrentlyActive);
    }

    @Override
    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
        listener.onSwiped(viewHolder, direction, viewHolder.getAdapterPosition());
    }

    @Override
    public int convertToAbsoluteDirection(int flags, int layoutDirection) {
        return super.convertToAbsoluteDirection(flags, layoutDirection);
    }

    public interface RecyclerItemTouchHelperListener {
        void onSwiped(RecyclerView.ViewHolder viewHolder, int direction, int position);
    }
}

12. Finally open MainActivity.java and do the changes as shown below.

> prepareCart() method fetches the json from the url, parses it and add the items to adapter data list

> The instance of RecyclerItemTouchHelper is created and assigned to RecyclerView. Here only LEFT direction is defined.

> onSwiped() method will be called when the swipe is performed. Here the important step of deleting the row item is taken place. mAdapter.removeItem() is called to delete the row from the RecyclerView.

> Once the row is deleted, Snackbar is used to show a message with UNDO option. Up on clicking UNDO, the row is restored back using mAdapter.restoreItem() method.

> deletedItem, deletedIndex variables are used to temporarily store the item deleted and the index until the Snackbar was shown.

import android.graphics.Color;
import android.os.Bundle;
import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.DividerItemDecoration;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.support.v7.widget.helper.ItemTouchHelper;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.widget.Toast;

import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.JsonArrayRequest;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

import org.json.JSONArray;

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

public class MainActivity extends AppCompatActivity implements RecyclerItemTouchHelper.RecyclerItemTouchHelperListener {

    private static final String TAG = MainActivity.class.getSimpleName();
    private RecyclerView recyclerView;
    private List<Item> cartList;
    private CartListAdapter mAdapter;
    private CoordinatorLayout coordinatorLayout;

    // url to fetch menu json
    private static final String URL = "https://api.androidhive.info/json/menu.json";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        getSupportActionBar().setTitle(getString(R.string.my_cart));
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);

        recyclerView = findViewById(R.id.recycler_view);
        coordinatorLayout = findViewById(R.id.coordinator_layout);
        cartList = new ArrayList<>();
        mAdapter = new CartListAdapter(this, cartList);

        RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext());
        recyclerView.setLayoutManager(mLayoutManager);
        recyclerView.setItemAnimator(new DefaultItemAnimator());
        recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
        recyclerView.setAdapter(mAdapter);

        // adding item touch helper
        // only ItemTouchHelper.LEFT added to detect Right to Left swipe
        // if you want both Right -> Left and Left -> Right
        // add pass ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT as param
        ItemTouchHelper.SimpleCallback itemTouchHelperCallback = new RecyclerItemTouchHelper(0, ItemTouchHelper.LEFT, this);
        new ItemTouchHelper(itemTouchHelperCallback).attachToRecyclerView(recyclerView);


        // making http call and fetching menu json
        prepareCart();
    }

    /**
     * method make volley network call and parses json
     */
    private void prepareCart() {
        JsonArrayRequest request = new JsonArrayRequest(URL,
                new Response.Listener<JSONArray>() {
                    @Override
                    public void onResponse(JSONArray response) {
                        if (response == null) {
                            Toast.makeText(getApplicationContext(), "Couldn't fetch the menu! Pleas try again.", Toast.LENGTH_LONG).show();
                            return;
                        }

                        List<Item> items = new Gson().fromJson(response.toString(), new TypeToken<List<Item>>() {
                        }.getType());

                        // adding items to cart list
                        cartList.clear();
                        cartList.addAll(items);

                        // refreshing recycler view
                        mAdapter.notifyDataSetChanged();
                    }
                }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                // error in getting json
                Log.d(TAG, "Error: " + error.getMessage());
                Toast.makeText(getApplicationContext(), "Error: " + error.getMessage(), Toast.LENGTH_SHORT).show();
            }
        });

        MyApplication.getInstance().addToRequestQueue(request);
    }

    /**
     * callback when recycler view is swiped
     * item will be removed on swiped
     * undo option will be provided in snackbar to restore the item
     */
    @Override
    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction, int position) {
        if (viewHolder instanceof CartListAdapter.MyViewHolder) {
            // get the removed item name to display it in snack bar
            String name = cartList.get(viewHolder.getAdapterPosition()).getName();

            // backup of removed item for undo purpose
            final Item deletedItem = cartList.get(viewHolder.getAdapterPosition());
            final int deletedIndex = viewHolder.getAdapterPosition();

            // remove the item from recycler view
            mAdapter.removeItem(viewHolder.getAdapterPosition());

            // showing snack bar with Undo option
            Snackbar snackbar = Snackbar
                    .make(coordinatorLayout, name + " removed from cart!", Snackbar.LENGTH_LONG);
            snackbar.setAction("UNDO", new View.OnClickListener() {
                @Override
                public void onClick(View view) {

                    // undo is selected, restore the deleted item
                    mAdapter.restoreItem(deletedItem, deletedIndex);
                }
            });
            snackbar.setActionTextColor(Color.YELLOW);
            snackbar.show();
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds cartList to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }
}

Run the project and test the app. Make sure your device is having active internet connection. Here is the screenshot of the final product.

I hope this article explained the swipe functionality very well. If you still have any queries, please post them in the comments section below.

Happy Coding 😀

Ravi is hardcore Android programmer and Android programming has been his passion since he compiled his first hello-world program. Solving real problems of Android developers through tutorials has always been interesting part for him.
  • Oops! I forgot to add that file. Now the article is updated. Please check point #9.

    Thanks for pointing that out.

    • Kid 1412

      Sorry, but apk download file error, you can update again .

  • Sourabh Jain

    Hello Ravi Sir,

    I have one problem I got an error in volley (outofmemory) when i refresh the list from new items if you have any code(help) please notify me. i m very gr8 full to you .. thanks in advance

    • Is the problem occurring in the same article?

      • Sourabh Jain

        yes i got it when i refresh it every 2 second for check new entry

        • Why you are refreshing it so frequently (2 seconds)?

          • Sourabh Jain

            Thanks For Reply

            Its live streaming market price application thats y i need to refresh it please help regarding this topic i m very thanks full to you

          • Okay, but thats not the way to show realtime data. Your app always crashes. Have you considered using Firebase Realtime database?

          • Sourabh Jain

            i m not using any database just use JSON to show Live Market Price

          • Any service that provides live json?

          • Sourabh Jain

            actually a php file convert xml to json and i call this php page every 2 second and show its data on app.

            If you have any idea to this work in different manner please notify me.

          • Making this process scalable is bit difficult. You can use Firebase Realtime Time db or FCM or Socket connections to handle this data in real time.

            If FCM is considered: (not optimal solution)
            When the data is changed, FCM sends notifications to device. Device will make the request to get the latest data.

            If Sockets considered: (Optimal, but scaling matters as maintaining lot of socket connections is difficult)
            The modified data will send in socket tunnel.

            If Firebase Db considered: (Best, works with any number of devices)
            Your php script inserts the modified data into firebase db. The app will automatically refreshes the data when it changed in Firebase db.

            Also you need to consider decreased the frequency. It shouldn’t be 2 secs. You can get started with firebase db here.
            https://www.androidhive.info/2016/10/android-working-with-firebase-realtime-database/

          • Sourabh Jain

            Thanks Ravi Sir,

            Tell me one thing more if i use FCM there is no need to call URL every 2 se

  • Priangga Emilsyah
    • What is compileSDK in build.gradle? It has to be 26.

    • Gamis

      You need to cast your view… Put the type… in this case is
      name = (TextView) view.findViewById(R.id.view)

      • Casting was removed in compileSdkVersion 26

  • Jeady Chatrola

    where did you parse json data??
    sir, what is the meaning of below line??

    List items = new Gson().fromJson(response.toString(), new TypeToken<List>() {
    }.getType());

    • Yes the json is serialized using Gson library.

  • Eyal Engel

    Thank you for the example.
    What makes the foreground be in front of the red background and hide it? I don’t see anything in the xml or programmatically…

    • It’s the order in which the elements are added to layout (top -> bottom). The bottom one always visible on Top. If you observe cart_list_item.xml, the foreground view is added at the bottom.

      • Eyal Engel

        Thanks. I forgot to explicitly add a background color (white) to the foreground’s RelativeLayout and that solved my problem.

  • Jochen

    Thanks for this example. Is it also possible to swipe only for the width of the trash icon and delete by touching the icon? Hope you can help me.

    • I have tried achieving that functionality but google has removed that user experience recently.

      • Jochen

        That’s bad. But thanks for your answer.

  • Neon Ds
  • Muhammad Alvin Ramadhan

    Hi Ravi, May i request a tutorial about RxJava 2 ?

  • Renan Bolonha

    Hi Ravi, Can u make a tutorial with ANIMATTION ? Like a when you click in a card and the same move to next activity something like that. Thank you. And I would like say to you, that you make a excelent job here. Congratulations.

  • Ravi Lukka

    Hi Ravi, Can we give swipe right to left & left to right in single item? If yes then please share me tutorial or changes.
    Thanks

    • You need to add RIGHT direction in itemTouchHelper ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT

      ItemTouchHelper.SimpleCallback itemTouchHelperCallback = new RecyclerItemTouchHelper(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT, this);
      new ItemTouchHelper(itemTouchHelperCallback).attachToRecyclerView(recyclerView);

  • Valdemar Colichon
    • Are you using Retrofit library?

      • Valdemar Colichon

        no, solo volley.

        • Valdemar Colichon

          tu mismo codigo como podria trabajarlo para que reciba result.

          • Gamis

            Yo recomiendo retrofit es mucho mas sencillo y mucho mejor que volley.

          • Learning Retrofit is little difficult in the beginning, but later it makes development much easier.

          • Juan Valdemar Colichon Ramirez

            with that same code as would

        • Consider using Retrofit, it will be much easier to parse.

  • Jerry Yu

    Hi Ravi,

    Can you explain why we need to overwrite onChildDrawOver and onSelectedChanged? Seems like only overwriting onChildDraw and clearView is enough for this effect. I commented out these two methods and it still works.

    Thanks,

    • If you remove onSelectedChanged(), is click assigned to foreground view working?

      • Jerry Yu

        Yes, in onBindViewHolder I set the viewholder as the listener for foregroundView onClick event.
        public void onBindViewHolder(ContactViewHolder holder, int position) {

        holder.foregroundView.setOnClickListener(holder);
        }
        Click events can be properly handled.

  • Jochen

    Thx, looks great. I wanna try this.