Adding Drag and Drop to RecyclerView Items in Android
A sortable list can make an Android app feel faster and more natural. Users may arrange shopping items, reorder tasks, customise a playlist, or move cards between positions with a long press. RecyclerView provides the efficient scrolling foundation, while touch handling and adapter updates create the drag-and-drop behaviour.
The most reliable approach for a reorderable list is AndroidX ItemTouchHelper. It connects to a RecyclerView, detects movement gestures, and reports position changes to the adapter. This keeps the implementation suitable for beginner projects while still supporting polished interactions in production applications used across Australia, from a Sydney delivery app to a Melbourne study planner.
| Approach | Best For | Strengths | Limitations |
|---|---|---|---|
ItemTouchHelper |
Reordering items in one list | Concise, tested, supports swipe and drag | Needs adapter and persistence logic |
| Custom touch events | Special gestures or unusual layouts | Complete control over movement | More code and edge cases |
| Drag handles | Desktop-style or accessibility-focused lists | Clear interaction target | Requires extra layout and touch handling |
| External drag and drop | Moving data between views or apps | Supports broader workflows | More complex than list reordering |
Prepare The RecyclerView Structure
Start with a model class containing a stable identifier, display text, and an ordering value if the list will be saved. A simple Kotlin data class might contain id, title, and position. Stable identifiers help Android animate changes correctly and reduce confusion when two entries have similar names.
The row layout should include a visible title and, where appropriate, a drag handle such as a vertical-dots icon. A handle is useful for Australian users managing lists on compact phones while commuting on Sydney trains or Melbourne trams, because it makes the intended gesture obvious. Keep the touch target comfortably sized and provide a content description for screen readers.
Create a RecyclerView.Adapter with a mutable list. Its onBindViewHolder method displays each item, while onCreateViewHolder inflates the row layout. If the app also contains ordinary screens, studying a small practical project such as this calculator app tutorial can help reinforce adapter, layout, and event-handling fundamentals.
Connect ItemTouchHelper
ItemTouchHelper.Callback is the central part of the implementation. Override getMovementFlags to allow dragging in the desired direction. For a vertical list, use ItemTouchHelper.UP or ItemTouchHelper.DOWN. Return zero for swipe directions when the application should reorder items without deleting them.
Override onMove and obtain the source and destination view holders. Convert their adapter positions into list indexes, then move the item and notify the adapter. A typical operation uses Collections.swap(items, fromPosition, toPosition) followed by notifyItemMoved(fromPosition, toPosition). Return true when the move is accepted.
Attach the helper after configuring the RecyclerView:
val callback = object : ItemTouchHelper.Callback() {
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,
source: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
adapter.moveItem(source.bindingAdapterPosition, target.bindingAdapterPosition)
return true
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) = Unit
}
ItemTouchHelper(callback).attachToRecyclerView(recyclerView)
Keep Adapter Positions Accurate
Position-related bugs are common when a list supports dragging, filtering, or deletion. Use bindingAdapterPosition at the moment an event occurs instead of relying on a position saved during binding. The position can be RecyclerView.NO_POSITION, so check it before changing the collection.
The adapter’s move method should update both the in-memory list and the visible rows:
fun moveItem(from: Int, to: Int) {
if (from == RecyclerView.NO_POSITION || to == RecyclerView.NO_POSITION) return
Collections.swap(items, from, to)
notifyItemMoved(from, to)
}
If the project uses ListAdapter and DiffUtil, submit a new list rather than mutating the existing submitted list. For a straightforward tutorial project, a mutable adapter is easier to understand. Larger applications should centralise list changes in a ViewModel, preventing rotation or process recreation from losing the current order.
Add Visual And Touch Feedback
Override onSelectedChanged to show which row is being dragged. Raising its elevation, changing its background colour, or adding a subtle shadow gives immediate feedback. Restore the original appearance in clearView, which runs when the gesture ends or is cancelled.
A long-press gesture is familiar to Android users, but a dedicated handle can provide greater precision. Configure the handle’s listener to start dragging with itemTouchHelper.startDrag(holder). Avoid attaching a click action to the entire row if it conflicts with scrolling or accidental movement.
Movement should remain smooth on lower-cost devices, including phones commonly used by families and small businesses outside Australia’s capital cities. Use lightweight row layouts, avoid expensive work inside onBindViewHolder, and keep image loading asynchronous. Haptic feedback can reinforce the start of a drag, but it should be optional and respectful of the device’s current settings.
Save The New Order
Dragging changes the visual order immediately, but the order will disappear after an activity restart unless it is stored. For a small list, SharedPreferences can save item identifiers in their current sequence. For structured data, use SQLite or Room and update each record’s sort value after the drag ends.
Saving on every onMove call may create unnecessary database work because one gesture can generate many position changes. A better pattern is to update the in-memory list during movement and persist the final order from clearView. If the list represents supermarket errands in Brisbane or recurring household tasks in Perth, preserving the arrangement between sessions makes the interaction genuinely useful.
When storing user-created content, consider the Australian Privacy Act 1988 and the Australian Privacy Principles if the list contains personal information. Avoid collecting more data than required, explain why information is saved, and protect synchronised data appropriately. Local storage is often sufficient for a simple reorderable list.
Test Accessibility And Edge Cases
Test dragging at the first and last positions, across a long list, during fast scrolling, and after items have been filtered or removed. Confirm that a cancelled gesture leaves the data consistent. Also test configuration changes, empty lists, duplicate titles, and restored data after the app is closed.
TalkBack users need an alternative to a precise swipe gesture. Provide an accessible action for moving an item up or down, expose the drag handle meaningfully, and announce the new position when the move completes. Ensure colour changes are not the only indication that a row is active, since contrast and visibility vary across devices and lighting conditions.
Finally, test on different screen sizes and Android versions, including devices used in regional areas where network access may be unreliable. A reorderable list should work without a connection once its data is available. With clear feedback, accurate adapter updates, and persistent ordering, RecyclerView drag and drop becomes a dependable feature rather than a fragile visual trick.