Drag And Drop Recyclerview Item Android

Drag And Drop Recyclerview Item Android [Example]

In this post, I have explained how to make a recyclerview item drag and dropable or swipeable by using the Android Support Library ItemTouchHelper.

Drag and drop recyclerview item with animation

before getting started, check out my other post on recyclerview.

Android RecyclerView Item Animations in Kotlin [Example]

Android Recyclerview Search Filter Example

Implementing Pagination With Recyclerview


ItemTouchHelper

This 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 the user performs these actions.

Depending on which functionality you support, you should override ItemTouchHelper.Callback.onMove(RecyclerView, ViewHolder, ViewHolder) for drag and drop.

ItemTouchHelper.Callback.onSwiped(ViewHolder, int) for swipe to dismiss.

We will be using the onMove method of callback in this article to move items in our recycler view from one position to another.

Step to make recyclerview item drag and dropable

  1. setup recyclerview item
  2. Setup recyclerview adapter
  3. Create a class for drag and drop
  4. Attach TouchHelper to Recyclerview
  5. Animate the Drag and Drop

Setup Recyclerview Item

in your mainactivity.xml create a recyclview and set up the layout manager and the adapter.

<androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerview"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:listitem="@layout/adapter_item"
        tools:itemCount="8"
        app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"/>Code language: HTML, XML (xml)

Now, we need to create the adapter layout for the recyclerview.

adapter_item.xml

<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    app:cardElevation="4dp"
    app:cardCornerRadius="4dp"
    android:layout_margin="8dp">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="8dp">

        <androidx.appcompat.widget.AppCompatImageView
            android:id="@+id/imgDragHandler"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:padding="8dp"
            app:tint="@color/black"
            android:src="@drawable/ic_baseline_drag_handle_24"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"/>


        <com.google.android.material.textview.MaterialTextView
            android:id="@+id/textTitle"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            style="@style/TextAppearance.AppCompat.Medium"
            android:textColor="@color/black"
            app:layout_constraintStart_toEndOf="@id/imgDragHandler"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            tools:text="@tools:sample/full_names"
            android:layout_margin="8dp" />

        <com.google.android.material.textview.MaterialTextView
            android:id="@+id/textDesc"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            style="@style/TextAppearance.AppCompat.Small"
            android:textColor="@color/black"
            app:layout_constraintStart_toEndOf="@id/imgDragHandler"
            app:layout_constraintTop_toBottomOf="@id/textTitle"
            app:layout_constraintEnd_toEndOf="parent"
            tools:text="@tools:sample/full_names"
            android:layout_margin="8dp"/>

    </androidx.constraintlayout.widget.ConstraintLayout>

</com.google.android.material.card.MaterialCardView>Code language: HTML, XML (xml)

It’s better the design the UI that has the handler for the drag and drop. it’s better for understandability.

Recyclerview item design

Setup recyclerview adapter

Create the recyclerview adapter class and add them AsyncListDiffer() to handle the data in the adapter. also, we need to attach them DiffUtil.ItemCallback() to it.

RecyclerviewAdapter.kt

class RecyclerviewAdapter : RecyclerView.Adapter<RecyclerviewAdapter.ItemViewHolder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder {
        val inflater = LayoutInflater.from(parent.context)
        val binding = AdapterItemBinding.inflate(inflater, parent, false)
        return ItemViewHolder(binding)
    }

    override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
        val movieItem= differ.currentList[position]
        holder.bindView(movieItem)
    }

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

    private val differCallback = object: DiffUtil.ItemCallback<User>() {
        override fun areItemsTheSame(oldItem: User, newItem: User): Boolean {
            return oldItem == newItem
        }

        override fun areContentsTheSame(oldItem: User, newItem: User): Boolean {
            return oldItem == newItem
        }

    }

    val differ = AsyncListDiffer(this, differCallback)

    fun moveItem(fromPosition: Int, toPosition: Int) {
        val list = differ.currentList.toMutableList()
        val fromItem = list[fromPosition]
        list.removeAt(fromPosition)
        if (toPosition < fromPosition) {
            list.add(toPosition + 1 , fromItem)
        } else {
            list.add(toPosition - 1, fromItem)
        }
        differ.submitList(list)
    }


    inner class ItemViewHolder(val binding: AdapterItemBinding) : RecyclerView.ViewHolder(binding.root) {

        fun bindView(item: User) {
            binding.apply {
                textTitle.text = item.name
                textDesc.text = item.location
            }
        }
    }
}Code language: HTML, XML (xml)

In the recyclerview adapter, I have added the moveItem() function to rearrange the adapter list item based on the recyclerview item position change during the drag and drop.

Create a class for drag and drop

Now in our MainActivity.kt Create a class for dragging by using the ItemTouchHelper.SimpleCallback() method. Simple callbacks need two parameters, one is the drag directions and another one is the swipe. Since we’re not implementing the swap directions in this tutorial so add “0” in the swipe parameter. And if you want to add the swipe parameter then go through my previous tutorial on swipe gestures in recyclerView.

For drag, if you’re using the down directions then add ItemTouchHelper.DOWN or if you want to use the up direction then we can use the ItemTouchHelper.UP. The application works even if you use UP and DOWN gestures but we will also use START and END. For that use ItemTouchHelper.START and ItemTouchHelper.END.

private val itemTouchHelper by lazy {
    val itemTouchCallback = object: ItemTouchHelper.SimpleCallback(UP or DOWN or START or END, 0) {
        override fun onMove(
            recyclerView: RecyclerView,
            viewHolder: RecyclerView.ViewHolder,
            target: RecyclerView.ViewHolder
        ): Boolean {
            
            return true
        }

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

        }
    }
    ItemTouchHelper(itemTouchCallback)
}Code language: JavaScript (javascript)

Here the onSwiped() a method used for swiping left and right. If you want to swipe your items in both directions in the recyclerView then we can use this method.

Since we’re only implementing the dragging behavior here, so will add our codes inside the onMove() method.

As we have moved the item between its initial and final position, we need to tell the adapter to change its position from these two points. So, we get the item’s position from the adapter and then the target position and let the adapter know that item has moved from position a to position b using notifyItemMoved().

private val itemTouchHelper by lazy {
    val itemTouchCallback = object: ItemTouchHelper.SimpleCallback(UP or DOWN or START or END, 0) {
        override fun onMove(
            recyclerView: RecyclerView,
            viewHolder: RecyclerView.ViewHolder,
            target: RecyclerView.ViewHolder
        ): Boolean {
            val recyclerviewAdapter = recyclerView.adapter as RecyclerviewAdapter
            val fromPosition = viewHolder.adapterPosition
            val toPosition = target.adapterPosition
            recyclerviewAdapter.moveItem(fromPosition, toPosition)
            recyclerviewAdapter.notifyItemMoved(fromPosition,toPosition)
            return true
        }

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

        }
        
    }
    ItemTouchHelper(itemTouchCallback)
}Code language: JavaScript (javascript)

Attach TouchHelper to Recyclerview

Already, we have created a recyclerview adapter, and touchHelper(). now we need to add both into the recyclerview on oncreate() function.

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    binding = ActivityMainBinding.inflate(layoutInflater)
    setContentView(binding.root)
    itemTouchHelper.attachToRecyclerView(binding.recyclerview)
    recyclerviewAdapter = RecyclerviewAdapter()
    recyclerviewAdapter.differ.submitList(getUsers())
    binding.recyclerview.adapter = recyclerviewAdapter
}

private fun getUsers() : List<User>{
    val users = mutableListOf<User>()
    users.add(User("John","Chennai",35))
    users.add(User("Mahesh","Pune",32))
    users.add(User("Palani","Bengalore",23))
    users.add(User("Kumar","Delhi",45))
    users.add(User("Dinesh","Mumbai",27))
    return users
}Code language: PHP (php)

Also, I have created sample data for the recyclerview.

output:

Drag and drop recyclerview item

Animate the Drag and Drop

To animate the recyclerview item drag and drop, we need to override the onSelectedChanged() and clearView() of the ItemTouchHelper.SimpleCallback().

In this example, I have animated the alpha and scale of the adapter view.

private val itemTouchHelper by lazy {
    val itemTouchCallback = object: ItemTouchHelper.SimpleCallback(UP or DOWN or START or END, 0) {
        override fun onMove(
            recyclerView: RecyclerView,
            viewHolder: RecyclerView.ViewHolder,
            target: RecyclerView.ViewHolder
        ): Boolean {
            val recyclerviewAdapter = recyclerView.adapter as RecyclerviewAdapter
            val fromPosition = viewHolder.adapterPosition
            val toPosition = target.adapterPosition
            recyclerviewAdapter.moveItem(fromPosition, toPosition)
            recyclerviewAdapter.notifyItemMoved(fromPosition,toPosition)
            return true
        }

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

        }

        override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
            super.onSelectedChanged(viewHolder, actionState)
            if(actionState == ACTION_STATE_DRAG) {
                viewHolder?.itemView?.scaleY = 1.3f
                viewHolder?.itemView?.alpha = 0.7f

            }
        }

        override fun clearView(
            recyclerView: RecyclerView,
            viewHolder: RecyclerView.ViewHolder
        ) {
            super.clearView(recyclerView, viewHolder)
            viewHolder.itemView.scaleY = 1.0f
            viewHolder?.itemView?.alpha = 1.0f
        }

    }
    ItemTouchHelper(itemTouchCallback)
}Code language: JavaScript (javascript)

output:

Drag and drop recyclerview item with animation

Thanks for reading. you can download this example on GitHub.


Posted

in

by

Comments

3 responses to “Drag And Drop Recyclerview Item Android [Example]”

  1. Yamini

    This code is not working.

    1. Whats is the error you are getting ?

      1. Shubham

        recyclerview items again come with previous position once will scroll.

Leave a Reply

Your email address will not be published. Required fields are marked *