Android RecyclerView Items Drag and Drop

RecyclerView is a ViewGroup ,that display a scrolling list of elements based on large data sets (or data that frequently changes). RecyclerView widget is more flexible and efficient version of ListView .

In the previous series of tutorials we have seen Basic Implementation of Android Recyclerview , In this tutorial we will see drag drop of RecyclerView items .



Lets see example of drag & drop for items of recyclerView

ItemTouchHelper :

ItemTouchHelper is a utility class to add swipe to dismiss and drag & drop support to RecyclerView. It works with a RecyclerView and a Callback class, which configures what type of interactions are enabled and also receives events when user performs these actions.

Add RecyclerView To XML Layout


Create XML Layout activity_main.xml , this will be set as content view for launcher Activity (MainActivity.kt) and add RecyclerView to your layout file .

file : activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/rcv"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

Data Class


To Load Data Into RecyclerView , We will read JSON File Kept Inside Asset folder and map it to above defined data class

file : src/main/assets/android_version.json



file : Model.kt
package com.tutorialsbuzz.recyclerviewdragdrop

data class Model(val name: String, val version: String) {}

ItemTouchHelper CallBack For RecyclerView 


Create an abstract class ItemMoveCallback extend this class to ItemTouchHelper.Callback override the getMovementFlags , onMove method .

1. getMovementFlags : Should return a composite flag which defines the enabled move directions in each state (idle, swiping, dragging).Instead of composing this flag manually, you can use makeMovementFlags(int, int) or makeFlag(int, int).
For example, if you want it to allow drag for UP and DOWN . 


2. onMove : Called when onMove(RecyclerView, ViewHolder, ViewHolder) returns true , Upon receiving this callback, you should move the item from the old position to target position .call onRowMoved fun of ItemTouchHelperContract which is implements by CustomAdapter  just swap the dragged position and call notifydatasetchange .


3. onSwiped :  Called when a ViewHolder is swiped by the user. At this point, you should update your adapter (e.g. remove the item) and call related Adapter notify event.


file: ItemMoveCallback.kt
package com.tutorialsbuzz.recyclerviewdragdrop

import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView

class ItemMoveCallback(private val mAdapter: ItemTouchHelperContract) :
    ItemTouchHelper.Callback() {

    override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {}

    override fun getMovementFlags(
        recyclerView: RecyclerView,
        viewHolder: RecyclerView.ViewHolder
    ): Int {
        val dragFlags = ItemTouchHelper.UP or ItemTouchHelper.DOWN
        return makeMovementFlags(dragFlags, 0)
    }

    override fun onMove(
        recyclerView: RecyclerView,
        viewHolder: RecyclerView.ViewHolder,
        target: RecyclerView.ViewHolder
    ): Boolean {
        mAdapter.onRowMoved(viewHolder.adapterPosition, target.adapterPosition)
        return true
    }

    interface ItemTouchHelperContract {
        fun onRowMoved(fromPosition: Int, toPosition: Int)
    }
}

Interface CallBack 


1. Create an interface ItemTouchHelperContract which is implements by CustomAdapter , inside onRowMoved just swap the dragged position and call notifydatasetchanged
  • onRowMoved called inside onMove of ItemMoveCallback .
  • Inside CustomAdapter onRowMoved which is overriden gets callback.
    interface ItemTouchHelperContract {
        fun onRowMoved(fromPosition: Int, toPosition: Int)
    }

2. Create an StartDragListener which is implements by MainActivity , inside requestDrag fun on touchHelper reference call startDrag fun by passing viewholder.
  • requestDrag called onTouch of imageView of recyclerView item .
  • Inside MainActivity requestDrag which is overriden gets callback.
	interface StartDragListener {
		fun requestDrag(viewHolder: RecyclerView.ViewHolder?)
	}

Adapter and ViewHolder For RecyclerView


1. XML Layout For RecyclerView Item

Create  XML Layout file in res/layout and name it row_item.xml .This Layout defines the layout for Items of RecyclerView . Here In this example we have two TextView inside LinearLayout .

file : row_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:paddingLeft="20dp"
    android:paddingTop="16dp"
    android:paddingRight="20dp"
    android:paddingBottom="16dp">

    <TextView
        android:id="@+id/txt"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:textSize="22sp" />

    <ImageView
        android:id="@+id/iv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/ic_baseline_reorder_24" />

</LinearLayout>
2. Adapter

Create a Adapter That RecyclerView Can Use , Create a class CustomAdapter extend it to RecyclerView.Adapter .

3. ViewHolder 

Create Inner class ViewHolder extend it to RecyclerView.ViewHolder

file : CustomAdapter.kt
package com.tutorialsbuzz.recyclerviewdragdrop

import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.View.OnTouchListener
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.row_item.view.*
import java.util.*

class CustomAdapter(
    val modelList: List<Model>,
    val mStartDragListener: StartDragListener?
) :
    RecyclerView.Adapter<RecyclerView.ViewHolder>(),
    ItemMoveCallback.ItemTouchHelperContract {

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        (holder as ViewHolder).bind(modelList.get(position));
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        val layoutInflater = LayoutInflater.from(parent.context)
        return ViewHolder(layoutInflater.inflate(R.layout.row_item, parent, false))
    }

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

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

        fun bind(model: Model): Unit {
            itemView.txt.text = model.name

            itemView.iv.setOnTouchListener(OnTouchListener { v, event ->
                if (event.action == MotionEvent.ACTION_DOWN)
                    mStartDragListener!!.requestDrag(this)

                false
            })
        }
    }

    override fun onRowMoved(fromPosition: Int, toPosition: Int) {

        if (fromPosition < toPosition) {
            for (i in fromPosition until toPosition) {
                Collections.swap(modelList, i, i + 1)
            }
        } else {
            for (i in fromPosition downTo toPosition + 1) {
                Collections.swap(modelList, i, i - 1)
            }
        }

        notifyItemMoved(fromPosition, toPosition)
    }
}


1. MainActivity

  • Create a koltin class MainActivity.kt and extend it to AppCompatActivity class .
  • Override onCreate function and set the content of this MainActivity with above defined xml layout (activity_main.xml). 
  • Read JSON Data From Asset Folder , this will be used for binding data into RecyclerView .
  • Set Vertical Linear LayoutManager to RecyclerView.
  • Create Instance of Adapter and set to RecyclerView .
  • Add CustomItemDecortion to RecyclerView .
  • Create an instance of ItemTouchHelper by passing ItemMoveCallback as parameter to its constructor.
  • Attach ItemTouchHelper to RecyclerView .
file : MainActivity.kt
package com.tutorialsbuzz.recyclerviewdragdrop

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.activity_main.*
import org.json.JSONArray
import org.json.JSONObject

class MainActivity : AppCompatActivity(), StartDragListener {

    var touchHelper: ItemTouchHelper? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val model = readFromAsset();
        val adapter = CustomAdapter(model, this)

        rcv.layoutManager = LinearLayoutManager(this, RecyclerView.VERTICAL, false)
        rcv.adapter = adapter;
        rcv.addItemDecoration(SimpleDividerItemDecoration(this@MainActivity))

        val callback: ItemTouchHelper.Callback = ItemMoveCallback(adapter)
        touchHelper = ItemTouchHelper(callback)
        touchHelper!!.attachToRecyclerView(rcv)
    }

    private fun readFromAsset(): List<Model> {

        val modeList = mutableListOf<Model>()
        val bufferReader = application.assets.open("android_version.json").bufferedReader()
        val json_string = bufferReader.use {
            it.readText()
        }

        val jsonArray = JSONArray(json_string);
        for (i in 0..jsonArray.length() - 1) {
            val jsonObject: JSONObject = jsonArray.getJSONObject(i)
            val model = Model(jsonObject.getString("name"), jsonObject.getString("version"))
            modeList.add(model)
        }
        return modeList
    }

    override fun requestDrag(viewHolder: RecyclerView.ViewHolder?) {
        touchHelper!!.startDrag(viewHolder!!)
    }
}


RecyclerView Related
  1. RecyclerView
  2. RecyclerView Item Click Ripple Effect
  3. RecyclerView With Item Divider
  4. RecyclerView With CardView
  5. RecyclerView GridLayout
  6. RecyclerView StaggeredGrid Layout
  7. RecyclerView Swipe To Delete
  8. Android RecyclerView Swipe to Delete Undo
  9. Android RecyclerView Interactive Swipe Like Gmail
  10. Android RecyclerView Buttons Under Swipe
  11. Android RecyclerView MultiViewType

No comments:

Post a Comment