Android RecyclerView Drag and Drop – Android Studio

RecyclerView – Drag and Drop using ItemTouchHelper

The Drag and Drop feature is possible using a RecyclerView Layout. The RecyclerView is an advanced View which holds a lot of helpful features built into it. With the help of ItemTouchHelper Utility class, you can achieve a lot of advanced Material UI Design motions. The ItemTouchHelper.Callback Class is very helpful in being a contract between the ItemTouchHelper and your RecyclerView. The UI Motions performed go out as a callback which gets recorded by the ItemTouchHelper utility class.

We will use this Class in the example below to create a drag and drop RecyclerView in Android Studio.

Creating the Layout

As always, it is time to create a sample layout that is holding the RecyclerView. Name it as recycler_layout.xml.

This will be a simple layout file containing one RecyclerView child to the LinearLayout parent. See the layout file below.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    tools:context=".MainActivity">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layoutManager="android.support.v7.widget.LinearLayoutManager" />

</LinearLayout>

Creating a RecyclerView holder layout

The RecyclerView should hold a simple Layout which will be repeated or recycled by the adapter we are going to create below. In simple terms, the RecyclerView is responsible for Recycling one layout file repeatedly, this way a list of layouts are created dynamically instead of a static creation of Views. This saves memory and improves performance. Name this file as list_items.xml 

The layout file is given below

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">
    <TextView
        android:id="@+id/name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="25sp"
        />
</RelativeLayout>

Creating the RecyclerView Adapter

Like we saw above, we need a mechanism to repeat our list_items.xml continuously inside our RecyclerView. This mechanism is done by the RecyclerView Adapter. You will have to create a simple Adapter to do that for you. Like the name suggests, adapter helps you to adapt the Data you have with the RecyclerView creating a Dynamic looking Layout.

Name the RecyclerView Adapter as RecyclerAdapter.java

package com.monks.android.newapplication;

import android.graphics.Color;
import android.support.annotation.NonNull;
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;
import java.util.Collections;

public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.MyViewHolder> implements ItemMoveCallback.ItemTouchHelperContract {

    private ArrayList data;

    public class MyViewHolder extends RecyclerView.ViewHolder {

        private TextView mTitle;
        View rowView;

        public MyViewHolder(View itemView) {
            super(itemView);

            rowView = itemView;
            mTitle = itemView.findViewById(R.id.name);
        }
    }

    public RecyclerAdapter(ArrayList data) {
        this.data = data;
    }

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

    @Override
    public void onBindViewHolder(MyViewHolder holder, int position) {
        holder.mTitle.setText(String.valueOf(data.get(position)));
    }


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


    @Override
    public void onRowMoved(int fromPosition, int toPosition) {
        if (fromPosition < toPosition) {
            for (int i = fromPosition; i < toPosition; i++) {
                Collections.swap(data, i, i + 1);
            }
        } else {
            for (int i = fromPosition; i > toPosition; i--) {
                Collections.swap(data, i, i - 1);
            }
        }
        notifyItemMoved(fromPosition, toPosition);
    }

    @Override
    public void onRowSelected(MyViewHolder myViewHolder) {
        myViewHolder.rowView.setBackgroundColor(Color.GRAY);

    }

    @Override
    public void onRowClear(MyViewHolder myViewHolder) {
        myViewHolder.rowView.setBackgroundColor(Color.WHITE);

    }
}

In the above adapter, we have inherited a class ItemMoveCallback.ItemTouchHelperContract. that is a custom created class to hold our ItemTouchHelper actions. We will see more about it in the next section.

Creating A ItemTouchHelper Class – Using the Callback

Once you have created the RecyclerView Adapter, it is time to jump into the important part of this tutorial, Creating a ItemTouchHelper Class. The ItemTouchHelper.Callback holds the methods for motion/actions like long press/itemswipe etc.

It also has the callback for methods like onMove(), onSwiped(), onSelectChanged(). We will be making changes in all of these methods which is overriden in our ItemMoveCallback class. See the code below for better understanding.

package com.monks.android.newapplication;

import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;

public class ItemMoveCallback extends ItemTouchHelper.Callback {

    private final ItemTouchHelperContract mAdapter;

    public ItemMoveCallback(ItemTouchHelperContract adapter) {
        mAdapter = adapter;
    }

    @Override
    public boolean isLongPressDragEnabled() {
        return true;
    }

    @Override
    public boolean isItemViewSwipeEnabled() {
        return false;
    }



    @Override
    public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int i) {

    }

    @Override
    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
        return makeMovementFlags(dragFlags, 0);
    }

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

    @Override
    public void onSelectedChanged(RecyclerView.ViewHolder viewHolder,
                                  int actionState) {


        if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
            if (viewHolder instanceof RecyclerAdapter.MyViewHolder) {
                RecyclerAdapter.MyViewHolder myViewHolder=
                        (RecyclerAdapter.MyViewHolder) viewHolder;
                mAdapter.onRowSelected(myViewHolder);
            }

        }

        super.onSelectedChanged(viewHolder, actionState);
    }
    @Override
    public void clearView(RecyclerView recyclerView,
                          RecyclerView.ViewHolder viewHolder) {
        super.clearView(recyclerView, viewHolder);

        if (viewHolder instanceof RecyclerAdapter.MyViewHolder) {
            RecyclerAdapter.MyViewHolder myViewHolder=
                    (RecyclerAdapter.MyViewHolder) viewHolder;
            mAdapter.onRowClear(myViewHolder);
        }
    }

    public interface ItemTouchHelperContract {

        void onBindViewHolder(RecyclerAdapter.MyViewHolder holder, int position);
        void onRowMoved(int fromPosition, int toPosition);
        void onRowSelected(RecyclerAdapter.MyViewHolder myViewHolder);
        void onRowClear(RecyclerAdapter.MyViewHolder myViewHolder);

    }
}

Creating the RecyclerActivity

Stitching all of the Classes together, we get the RecyclerActivity. The RecyclerActivity creates the adapter, also creates the ItemTouchHelper instance and provides us with an interface to control the movements.

Upon completion, the final structure looks like below,

package com.monks.android.newapplication;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
import android.util.Log;

import java.util.ArrayList;

public class RecyclerActivity extends AppCompatActivity {
    RecyclerView recyclerView;
    RecyclerAdapter  mAdapter;
    ArrayList<String> stringArrayList = new ArrayList<>();
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.recycler_view);
        recyclerView = findViewById(R.id.recyclerView);
        stringArrayList.add("AndroidMonks");
        stringArrayList.add("TextView");
        stringArrayList.add("Buttons");
        stringArrayList.add("EditText");
        stringArrayList.add("ImageView");
        stringArrayList.add("Relative Layout");
        stringArrayList.add("Linear Layout");
        //      add the adapter here
        mAdapter = new RecyclerAdapter(stringArrayList);
        //      Create the ItemTouchHelper
        ItemTouchHelper.Callback callback =
                new ItemMoveCallback(mAdapter);
        ItemTouchHelper touchHelper = new ItemTouchHelper(callback);
        touchHelper.attachToRecyclerView(recyclerView);

        recyclerView.setAdapter(mAdapter);
    }
}

Once done, lets fire up our AVD and run the application.

Final Result

Drag and Drop RecyclerView
Drag and Drop RecyclerView

Leave a Comment

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