Let’s assume you are creating some cool notes app and you want to give users the ability to drag and drop the individual notes item and save their final positions let’s say in a database. Well the first step in that is understanding how to drag drop items in an adapterview, e.g listview, gridview, recyclerview.

In this article you will learn:

  1. How to drag and drop items in a grid.
  2. How to get the items final position.

We use the following languages:

  1. Kotlin
  2. Java

NB/= This article is broken down into several pages. Each page teaches a unique concept. Here are the taught concepts:

  1. Drag Drop in a Grid
  2. Drag Drop in a RecyclerView

Concept 1: Drag Drop in Grid

Example 1: Drap Drop Items in a GridView

Let us see how to drag drop items in a GridView.

Step 1: Dependencies

We use no third party dependencies.

Step 2: Code

We write our code in Java.

We have two classes, a fragment hosted inside an activity:

MainFragment.java

package info.camposha.dragdropexample;

/**
 * ANDROID: http://www.camposha.info : Oclemy.
 */

import android.content.ClipData;
import android.graphics.Color;
import android.os.Bundle;
import android.view.DragEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;

import androidx.fragment.app.Fragment;

public class MainFragment extends Fragment {

    private View[] views;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.fragment_main, container, false);
        views = new View[rootView.getChildCount()];
        for (int i = 0; i < rootView.getChildCount(); i++) {
            final TextView view = (TextView) rootView.getChildAt(i);
            initView(view);
            views[i] = view;
        }
        return rootView;
    }

    private void initView(final TextView view) {
        view.setOnDragListener((v, event) -> {
            switch(event.getAction()) {
                case DragEvent.ACTION_DRAG_STARTED:
                    return true;

                case DragEvent.ACTION_DRAG_ENTERED:
                    v.setBackgroundColor(Color.LTGRAY);
                    return true;

                case DragEvent.ACTION_DRAG_LOCATION:
                    return true;

                case DragEvent.ACTION_DRAG_EXITED:
                    v.setBackgroundColor(Color.TRANSPARENT);
                    return true;

                case DragEvent.ACTION_DROP:
                    v.setBackgroundColor(Color.TRANSPARENT);
                    int dragVal = Integer.parseInt(event.getClipData().getItemAt(0).getText().toString());
                    int viewVal = Integer.parseInt(((TextView) v).getText().toString());
                    ((TextView) v).setText("" + (dragVal + viewVal));
                    Toast.makeText(getContext(), "Added", Toast.LENGTH_SHORT).show();
                    return true;

                case DragEvent.ACTION_DRAG_ENDED:
                    return true;

                default:
                    break;
            }
            return false;
        });

        view.setOnLongClickListener(v -> {
            ClipData data = ClipData.newPlainText("value", view.getText());
            view.startDrag(data, new View.DragShadowBuilder(v), null, 0);
            return true;
        });
    }

}

MainActivity

To host our fragment:

package info.camposha.dragdropexample;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

Step 3: Layouts

Copy the layout from the source code.

Step 4: Run

If your run the project you’ll get the following:

Step 5: Download

Download the full source code here.

Example 2: Kotlin Android Drag Drop

This is a simple example showing howto implement drag and drop in Kotlin.

Step 1: Create Project

Start by creating an empty Android Studio project.

Step 2: Dependencies

No third party library is needed for our implementation of Drag Drop.

Step 3: Create a Drag and Drop Container

You need to create a custom Drag and drop layout. This layout is based on the FrameLayout and will be used in our layout:

DragAndDropContainer.kt

package supahsoftware.androidexampledragdrop

import android.content.ClipData
import android.content.ClipDescription
import android.content.Context
import android.os.Build
import android.util.AttributeSet
import android.view.View
import android.widget.FrameLayout

class DragAndDropContainer(
    context: Context,
    attrs: AttributeSet?
) : FrameLayout(context, attrs) {

    private val dragAndDropListener by lazy { DragAndDropListener() }
    private var content: View? = null

    init {
        setOnDragListener(dragAndDropListener)
    }

    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
        super.onLayout(changed, left, top, right, bottom)
        updateChild()
    }

    fun setContent(view: View) {
        removeAllViews()
        addView(view)
        updateChild()
    }

    fun removeContent(view: View) {
        view.setOnLongClickListener(null)
        removeView(view)
        updateChild()
    }

    private fun updateChild() {
        validateChildCount()
        content = getFirstChild()
        content?.setOnLongClickListener { startDrag() }
    }

    private fun getFirstChild() = if (childCount == 1) getChildAt(0) else null

    private fun validateChildCount() = check(childCount <= 1) {
        "There should be a maximum of 1 child inside of a DragAndDropContainer, but there were $childCount"
    }

    private fun startDrag(): Boolean {
        content?.let {
            val tag = it.tag as? CharSequence
            val item = ClipData.Item(tag)
            val data = ClipData(tag, arrayOf(ClipDescription.MIMETYPE_TEXT_PLAIN), item)
            val shadow = DragShadowBuilder(it)

            if (Build.VERSION.SDK_INT >= 24) {
                it.startDragAndDrop(data, shadow, it, 0)
            } else {
                it.startDrag(data, shadow, it, 0)
            }
            return true
        } ?: return false
    }
}

Step 4: Design Layout

Design your MainActivity’s layout as follows:

activity_main.xml

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

    <supahsoftware.androidexampledragdrop.DragAndDropContainer
        android:id="@+id/container_1"
        android:layout_width="match_parent"
        android:layout_height="120dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:background="@drawable/outline_gray_solid"
        app:layout_constraintTop_toTopOf="parent">

        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <ImageView
                android:id="@+id/content_image"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:src="@drawable/banner"
                android:scaleType="center" />

        </FrameLayout>

    </supahsoftware.androidexampledragdrop.DragAndDropContainer>

    <supahsoftware.androidexampledragdrop.DragAndDropContainer
        android:id="@+id/container_2"
        android:layout_width="match_parent"
        android:layout_height="120dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:background="@drawable/outline_gray_solid"
        app:layout_constraintTop_toBottomOf="@id/container_1" />

    <supahsoftware.androidexampledragdrop.DragAndDropContainer
        android:id="@+id/container_3"
        android:layout_width="match_parent"
        android:layout_height="120dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:background="@drawable/outline_gray_solid"
        app:layout_constraintTop_toBottomOf="@id/container_2" />

    <supahsoftware.androidexampledragdrop.DragAndDropContainer
        android:id="@+id/container_4"
        android:layout_width="match_parent"
        android:layout_height="120dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:background="@drawable/outline_gray_solid"
        app:layout_constraintTop_toBottomOf="@id/container_3" />

</androidx.constraintlayout.widget.ConstraintLayout>

Step 5: Create a Drag and Drop Listener

As follows:

DragAndDropListener.kt

package supahsoftware.androidexampledragdrop

import android.content.ClipDescription
import android.view.DragEvent
import android.view.View
import androidx.core.content.ContextCompat

class DragAndDropListener : View.OnDragListener {
    override fun onDrag(view: View, event: DragEvent): Boolean {
        return when (event.action) {
            DragEvent.ACTION_DRAG_ENTERED -> {
                view.setDashedOutline(); true
            }
            DragEvent.ACTION_DRAG_EXITED -> {
                view.setSolidOutline(); true
            }
            DragEvent.ACTION_DROP -> {
                val draggingView = event.localState as View
                val draggingViewParent = draggingView.parent as DragAndDropContainer
                draggingViewParent.removeContent(draggingView)

                val landingContainer = view as DragAndDropContainer
                landingContainer.setContent(draggingView)
                landingContainer.setSolidOutline()
                true
            }
            DragEvent.ACTION_DRAG_STARTED -> event.clipDescription.hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN)
            else -> true
        }
    }

    private fun View.setSolidOutline() {
        background = ContextCompat.getDrawable(context, R.drawable.outline_gray_solid)
    }

    private fun View.setDashedOutline() {
        background = ContextCompat.getDrawable(context, R.drawable.outline_gray_dashed)
    }
}

Step 6: Create MainActivity

Create MainActivity as follows:

MainActivity.kt

package supahsoftware.androidexampledragdrop

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity

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

Run

Copy the code or download it in the link below, build and run.

Reference

Here are the reference links:

Number Link
1. Download Example
2. Follow code author
3. Code: Apache 2.0 License

Concept 2: Drag and Drop in RecyclerView