Kotlin RecyclerView Swipe To Delete Android Like Gmail

kotlin sqlite multiple tables, kotlin recyclerview checkbox, kotlin recyclerview button, kotlin load html javascript, kotlin recyclerview searchview, kotlin recyclerview search using edittext, kotlin listview with edittext, kotlin recyclerview with edittext, kotlin swipe to delete listview, kotlin recyclerview swipe to delete, kotlin expandablelistview with checkbox, kotlin custom ratingbar, kotlin change tab, Kotlin Select Multiple Images From Gallery, Kotlin Custom Dialog With Image And Title, kotlin select multiple video

Let us make Kotlin RecyclerView Swipe To Delete Android Like Gmail App.

You will learn to create recycler view swipe to show delete and undo button example.

It is easy for users to just swipe left or right to delete any row from the recycler view.

You can use this feature for flexibility as well as simplicity.

First of all, see the following video.

 

Step 1. New Project and Gradle

In your android studio, create a new project with empty activity.

Also, do not forget to set Kotlin as the primary language for your project.

Now open your build.gradle(Module: app) file. Add the following lines in this file

 implementation 'com.android.support:recyclerview-v7:28.0.0'
    implementation 'com.android.support:cardview-v7:28.0.0'
    implementation 'com.android.support:design:28.0.0'

First line will give us access to the classes of the recycler view.

Similarly, second line will allow us to use card view along with the recycler view.

Finally, last line is for material design. We are implementing these lines because android do not have any UI widgets for them like text view or Image View.

Step 2. Add Delete Image

First, download the Delete Icon by clicking the below link.

Click to Download Delete Icon.

After downloading the above image, copy it into the app->res->drawable directory.

Step 3. XML File and Model Class

Inside app->res->layout directory, make a new XML file and give it a name like rv_item.xml

rv_item.xml file should have the following source lines

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingBottom="5dp"
    android:paddingLeft="5dp"
    android:paddingRight="5dp"
    tools:context=".MainActivity"
    tools:ignore="NamespaceTypo">

    <android.support.v7.widget.CardView xmlns:card_view="http://schemas.android.com/apk/res-auto"
        android:id="@+id/card_view"
        android:layout_width="match_parent"
        android:layout_height="90dp"
        android:layout_gravity="center"
        app:cardBackgroundColor="#ffffff"
        android:layout_marginTop="0dp"
        card_view:cardCornerRadius="1dp">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:id="@+id/tv"
                android:height="90dp"
                android:gravity="center"
                android:paddingLeft="10dp"
                android:text="Image"
                android:textColor="#000"
                android:textStyle="bold"
                android:textSize="18sp" />

        </LinearLayout>

    </android.support.v7.widget.CardView>
</RelativeLayout>

This file has one text view inside the card view. Thus, every row of recycler view will have the card view along with one text view.

Now make a new class and give it a name like SwipeModel.kt

SwipeModel.kt have the following source snippet

class Model {

    var name: String? = null


    fun getNames(): String {
        return name.toString()
    }

    fun setNames(name: String) {
        this.name = name
    }
}

One string variable is there in the above code. It will print the name of the vehicle in the recycler view.

For this string variable, appropriate getter and setter methods are also there which will help us in the data maintenance.

Step 4. Adapter For Swipe

Make a new class and give it a name as SwipeAdapter.kt

Below is the coding snippet for SwipeAdapter.kt file.

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.TextView
import java.util.ArrayList

/**
 * Created by Parsania Hardik on 26-Jun-17.
 */
class SwipeAdapter(ctx: Context, private val imageModelArrayList: ArrayList<Model>) :
    RecyclerView.Adapter<SwipeAdapter.MyViewHolder>() {

    private val inflater: LayoutInflater


    init {

        inflater = LayoutInflater.from(ctx)
    }

    fun removeItem(position: Int) {
        imageModelArrayList.removeAt(position)
        notifyItemRemoved(position)
        notifyItemRangeChanged(position, imageModelArrayList.size)
    }

    fun restoreItem(model: Model, position: Int) {
        imageModelArrayList.add(position, model)
        // notify item added by position
        notifyItemInserted(position)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {

        val view = inflater.inflate(R.layout.rv_item, parent, false)

        return MyViewHolder(view)
    }

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {

        holder.time.setText(imageModelArrayList[position].getNames())
    }

    override fun getItemCount(): Int {
        return imageModelArrayList.size
    }

     inner class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {

        var time: TextView


        init {

            time = itemView.findViewById(R.id.tv) as TextView

        }

    }
}

A method onCreateViewHolder() will inflate the rv_item.xml file to create the view of every row.

Then onBindViewHolder() method will set the vehicle name into the text view. For this, it will use imageModelArrayList as a data source.

imageModelArrayList is the arraylist with the objects of the SwipeModel class. Adapter is getting it from the parameters.

Now see the following function

  fun removeItem(position: Int) {
        imageModelArrayList.removeAt(position)
        notifyItemRemoved(position)
        notifyItemRangeChanged(position, imageModelArrayList.size)
    }

This function will be called when the user swipes left or right and do not press undo button.

Compiler will remove the object of the position which user have swiped. (If first row then position will be 0)

Then after notifyItemRemoved() method will be called and finally, notifyItemRangeChanged() method will refresh the recycler view.

Now read the following function

 fun restoreItem(model: Model, position: Int) {
        imageModelArrayList.add(position, model)
        // notify item added by position
        notifyItemInserted(position)
    }

When the user clicks the Undo button after deleting the row, compiler will execute the below method.

It will first add the deleted object and then it will run notifyItemInserted() method to refresh the recycler view.

Step 5. Changing Main Lines

There are two files for main activity. You need to add the following lines in activity_main.xml file.

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/linear"
        android:background="#e4e0e0"
        tools:context=".MainActivity">

    <android.support.v7.widget.RecyclerView
            android:id="@+id/recycler"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginTop="10dp"/>

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

This main file have just one recycler view.

Now write down the below source code lines in MainActivity.kt file.

import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Color
import android.graphics.Paint
import android.graphics.RectF
import android.support.design.widget.Snackbar
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.graphics.Canvas
import java.util.ArrayList
import android.support.v7.widget.helper.ItemTouchHelper

class MainActivity : AppCompatActivity() {

    private var recyclerView: RecyclerView? = null
    private var imageModelArrayList: ArrayList<Model>? = null
    private var adapter: SwipeAdapter? = null
    private val p = Paint()
    private val myImageNameList = arrayOf("Benz", "Bike", "Car", "Carrera", "Ferrari", "Harly", "Lamborghini", "Silver")

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        recyclerView = findViewById(R.id.recycler) as RecyclerView

        imageModelArrayList = populateList()

        adapter = SwipeAdapter(this, imageModelArrayList!!)
        recyclerView!!.adapter = adapter
        recyclerView!!.layoutManager = LinearLayoutManager(applicationContext, LinearLayoutManager.VERTICAL, false)

        enableSwipe()
    }

    private fun enableSwipe() {
        val simpleItemTouchCallback =
            object : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT) {

                override fun onMove(
                    recyclerView: RecyclerView,
                    viewHolder: RecyclerView.ViewHolder,
                    target: RecyclerView.ViewHolder
                ): Boolean {
                    return false
                }

                override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
                    val position = viewHolder.adapterPosition

                    if (direction == ItemTouchHelper.LEFT) {
                        val deletedModel = imageModelArrayList!![position]
                        adapter!!.removeItem(position)
                        // showing snack bar with Undo option
                        val snackbar = Snackbar.make(
                            window.decorView.rootView,
                            " removed from Recyclerview!",
                            Snackbar.LENGTH_LONG
                        )
                        snackbar.setAction("UNDO") {
                            // undo is selected, restore the deleted item
                            adapter!!.restoreItem(deletedModel, position)
                        }
                        snackbar.setActionTextColor(Color.YELLOW)
                        snackbar.show()
                    } else {
                        val deletedModel = imageModelArrayList!![position]
                        adapter!!.removeItem(position)
                        // showing snack bar with Undo option
                        val snackbar = Snackbar.make(
                            window.decorView.rootView,
                            " removed from Recyclerview!",
                            Snackbar.LENGTH_LONG
                        )
                        snackbar.setAction("UNDO") {
                            // undo is selected, restore the deleted item
                            adapter!!.restoreItem(deletedModel, position)
                        }
                        snackbar.setActionTextColor(Color.YELLOW)
                        snackbar.show()
                    }
                }

                override fun onChildDraw(
                    c: Canvas,
                    recyclerView: RecyclerView,
                    viewHolder: RecyclerView.ViewHolder,
                    dX: Float,
                    dY: Float,
                    actionState: Int,
                    isCurrentlyActive: Boolean
                ) {

                    val icon: Bitmap
                    if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {

                        val itemView = viewHolder.itemView
                        val height = itemView.bottom.toFloat() - itemView.top.toFloat()
                        val width = height / 3

                        if (dX > 0) {
                            p.color = Color.parseColor("#388E3C")
                            val background =
                                RectF(itemView.left.toFloat(), itemView.top.toFloat(), dX, itemView.bottom.toFloat())
                            c.drawRect(background, p)
                            icon = BitmapFactory.decodeResource(resources, R.drawable.delete)
                            val icon_dest = RectF(
                                itemView.left.toFloat() + width,
                                itemView.top.toFloat() + width,
                                itemView.left.toFloat() + 2 * width,
                                itemView.bottom.toFloat() - width
                            )
                            c.drawBitmap(icon, null, icon_dest, p)
                        } else {
                            p.color = Color.parseColor("#D32F2F")
                            val background = RectF(
                                itemView.right.toFloat() + dX,
                                itemView.top.toFloat(),
                                itemView.right.toFloat(),
                                itemView.bottom.toFloat()
                            )
                            c.drawRect(background, p)
                            icon = BitmapFactory.decodeResource(resources, R.drawable.delete)
                            val icon_dest = RectF(
                                itemView.right.toFloat() - 2 * width,
                                itemView.top.toFloat() + width,
                                itemView.right.toFloat() - width,
                                itemView.bottom.toFloat() - width
                            )
                            c.drawBitmap(icon, null, icon_dest, p)
                        }
                    }
                    super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive)
                }
            }
        val itemTouchHelper = ItemTouchHelper(simpleItemTouchCallback)
        itemTouchHelper.attachToRecyclerView(recyclerView)
    }

    private fun populateList(): ArrayList<Model> {

        val list = ArrayList<Model>()

        for (i in 0..7) {
            val imageModel = Model()
            imageModel.setNames(myImageNameList[i])
            list.add(imageModel)
        }

        return list
    }
}

Diving In Main Kotlin

First of all, see the below

  private var recyclerView: RecyclerView? = null
    private var imageModelArrayList: ArrayList<Model>? = null
    private var adapter: SwipeAdapter? = null
    private val p = Paint()
    private val myImageNameList = arrayOf("Benz", "Bike", "Car", "Carrera", "Ferrari", "Harly", "Lamborghini", "Silver")

First, compiler will create the object of recycler view, Swipe adapter, Paint classes.

An array with the objects of an Model class is imageModelArrayList

Another string array contains the names of the vehicles.

Now read the below lines

  imageModelArrayList = populateList()

        adapter = SwipeAdapter(this, imageModelArrayList!!)
        recyclerView!!.adapter = adapter
        recyclerView!!.layoutManager = LinearLayoutManager(applicationContext, LinearLayoutManager.VERTICAL, false)
         enableSwipe()

Compiler will use the populateList() method to fill the data into imageModelArrayList

Then it will bind the adapter to the recycler view. After this, compiler will call enableSwipe() method.

Look at the below snippet from enableSwipe() method.

 override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
                    val position = viewHolder.adapterPosition

                    if (direction == ItemTouchHelper.LEFT) {
                        val deletedModel = imageModelArrayList!![position]
                        adapter!!.removeItem(position)
                        // showing snack bar with Undo option
                        val snackbar = Snackbar.make(
                            window.decorView.rootView,
                            " removed from Recyclerview!",
                            Snackbar.LENGTH_LONG
                        )
                        snackbar.setAction("UNDO") {
                            // undo is selected, restore the deleted item
                            adapter!!.restoreItem(deletedModel, position)
                        }
                        snackbar.setActionTextColor(Color.YELLOW)
                        snackbar.show()
                    } else {
                        val deletedModel = imageModelArrayList!![position]
                        adapter!!.removeItem(position)
                        // showing snack bar with Undo option
                        val snackbar = Snackbar.make(
                            window.decorView.rootView,
                            " removed from Recyclerview!",
                            Snackbar.LENGTH_LONG
                        )
                        snackbar.setAction("UNDO") {
                            // undo is selected, restore the deleted item
                            adapter!!.restoreItem(deletedModel, position)
                        }
                        snackbar.setActionTextColor(Color.YELLOW)
                        snackbar.show()
                    }
                }

Compiler will call the above onSwiped() method when the user swipes any row to the left or right side.

Then it will check one if condition for detecting the left or right swipe. Compiler will first get the object which it wants to remove.

Then it will execute removeItem() method from the adapter class. After this, it will show one snack bar.

When the user clicks the UNDO button on snackbar, compiler will call restoreItem() method from adapter.

Download Kotlin RecyclerView Swipe to Delete

https://github.com/demonuts/Kotlin-RecyclerView-Swipe-To-Delete-Android-Like-Gmail