Search code examples
androidandroid-recyclerviewdragitemtouchhelper

android limit item dragging in a range itemtouchhelper


I have show a list of Items with RecyclerView and I have items of different types (headers and items), like the following

Header 1
Item 1
Item 2
Header 2
Item 3
Item 4 
Item 5
Item 6
Header 3
Item 7
....

I would like to limit swapping the position of Items inside their section Headers. So for example, Item 4 can be swapped with Item 3, Item 5 and Item 6 but not go outside it's section boundaries. I'm using ItemTouchHelper to implement the swap on drag mechanism as suggested here https://medium.com/@ipaulpro/drag-and-swipe-with-recyclerview-b9456d2b1aaf and https://medium.com/@ipaulpro/drag-and-swipe-with-recyclerview-6a6f0c422efd I overloaded the canDropOver method which helps if you want to swap for example Item 4 and Header 2 but not in preventing me in dragging the Item till Header 1. Any suggestions?


Solution

  • Once you've got canDropOver set, the last piece of the puzzle is to override onChildDraw and clamp the dY value (assuming a vertical list -- clamp the dX value if using a horizontal list) whenever a ViewHolder is being dragged toward an adjacent ViewHolder it can't drop over.

    For example:

        override fun onChildDraw(
            c: Canvas,
            recyclerView: RecyclerView,
            viewHolder: RecyclerView.ViewHolder,
            dX: Float,
            dY: Float,
            actionState: Int,
            isCurrentlyActive: Boolean
        ) {
            val previousViewHolder = recyclerView.findViewHolderForAdapterPosition(viewHolder.bindingAdapterPosition - 1)
            val nextViewHolder = recyclerView.findViewHolderForAdapterPosition(viewHolder.bindingAdapterPosition + 1)
            val isDraggingUpward = dY < 0
            val isDraggingDownward = dY > 0
    
            val isDraggingIntoUndraggableArea =
                (isDraggingUpward && previousViewHolder != null && !canDropOver(recyclerView, viewHolder, previousViewHolder))
                || (isDraggingDownward && nextViewHolder != null && !canDropOver(recyclerView, viewHolder, nextViewHolder))
    
            val newDy = if (isDraggingIntoUndraggableArea) {
                0f  // Clamp
            } else {
                dY
            }
    
            super.onChildDraw(c, recyclerView, viewHolder, dX, newDy, actionState, isCurrentlyActive)
        }
    

    I've also got a repository you can refer to which has a full app example: https://github.com/nihk/ClampedItemTouchHelper