Search code examples
androidkotlinandroid-studioandroid-recyclerviewdrag-and-drop

Recyclerview drag and drop goes crazy with many items


I'm working on my time management app Mission Control, built in Kotlin Android studio. I have an option to create a list of tasks using recyclerview and I implemented drag and drop to enable replacing the order, which works perfect if I have only a few items, in the attached gif I show how it gets very hard to drag and drop when there are more items in the list.. Anyone has any clue on how to solve this behavior? I tried using different drag and drop functions but nothing seems to solve it.

Example

In case you want to give the app a try it's free here: link

My code:

 //Implementation of drag and drop - Vertical in a task list combined with
            // the implementation of the swipe

            //Devider between items
            val dividerItemDecoration =
                DividerItemDecoration(this, DividerItemDecoration.VERTICAL)

            // Remove all existing item decorations - important!
            for (i in rvTaskList?.itemDecorationCount!! - 1 downTo 0) {
                rvTaskList?.removeItemDecorationAt(i)
            }

            rvTaskList?.addItemDecoration(dividerItemDecoration)

            //Resetting drag positions
            var mPositionDraggedFrom = -1
            var mPositionDraggedTo = -1

            //Remove former helpers - important!
            itemTouchHelper?.attachToRecyclerView(null)

            val helper = ItemTouchHelper(object : SwipeHelper(rvTaskList) {

                override fun getMovementFlags(
                    recyclerView: RecyclerView,
                    viewHolder: RecyclerView.ViewHolder
                ): Int {
                    //Decides the touch direction - swipe or drag to replace
                    return makeMovementFlags(
                        ItemTouchHelper.UP or ItemTouchHelper.DOWN,
                        ItemTouchHelper.LEFT
                    )
                }

                override fun instantiateUnderlayButton(position: Int): List<UnderlayButton> {
                    //Creates the swiped buttons
                    var buttons = listOf<UnderlayButton>()
                    val deleteButton = deleteButton(position)
                    val editButton = editButton(position)
                    buttons = listOf(deleteButton, editButton)

                    return buttons
                }

                override fun onMove(
                    recyclerView: RecyclerView,
                    dragged: RecyclerView.ViewHolder,
                    target: RecyclerView.ViewHolder
                ): Boolean {
                    //Moving items in the rv
                    val draggedPosition = dragged.adapterPosition
                    val targetPosition = target.adapterPosition

                    if (mPositionDraggedFrom == -1) {
                        mPositionDraggedFrom = draggedPosition
                    }
                    mPositionDraggedTo = targetPosition
                    //Changing the location in a list from the dragged tp target
                    Collections.swap(
                        mPlanDetails.dayPlanList[mDayPlanPosition].tasks,
                        draggedPosition,
                        targetPosition
                    )
                    taskAdapter.notifyItemMoved(draggedPosition, targetPosition)
                    return false
                }

                override fun clearView(
                    //Called once the drag and drop is over
                    recyclerView: RecyclerView,
                    viewHolder: RecyclerView.ViewHolder
                ) {
                    super.clearView(recyclerView, viewHolder)

                    if (mPositionDraggedFrom != -1 && mPositionDraggedTo != -1 &&
                        mPositionDraggedFrom != mPositionDraggedTo
                    ) {
                        updateTasksInDayPlan(
                            mDayPlanPosition, mPlanDetails.dayPlanList[mDayPlanPosition].tasks
                        )
                    }
                }

            })

            //Attach the helper
            itemTouchHelper = helper
            itemTouchHelper?.attachToRecyclerView(rvTaskList)

Solution

  • onMove method should return true according to the documentation:

    "If this method returns true, ItemTouchHelper assumes viewHolder has been moved to the adapter position of target ViewHolder"

    onMove ItemTouchHelper