In Android 10, the RecyclerView item drag-and-swap method is difficult to trigger (ItemTouchHelper.callback onMove)

Viewed 37

This is my implementation of ItemTouchHelper.callback

I implemented getMovementFlags so that it could drag and drop in UP, Down, left, and right directions but didn't execute the onMove method

I know where the problem is because I'm in the setOnLongClickListener notifyDataSetChanged on the item and that causes it to fail to drag but I do need to go through setOnLongClickListener, okay NotifyDataSetChanged updates the item to editable style

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

    @Override
    public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
        int fromPosition = viewHolder.getAdapterPosition(); 
        int toPosition = target.getAdapterPosition(); 
        dragAdapter.itemMove(fromPosition, toPosition);
        return true;
    }
1 Answers

LEFT and RIGHT should be used for swipe, so keep UP and DOWN only for move position.

And, onMove will be called many many times, so you should implement clearView and do the move in clearView. onMove is just used to record positions.

private val _callback = object:ItemTouchHelper.Callback() {
    override fun getMovementFlags(rv: RecyclerView, h: RecyclerView.ViewHolder): Int {
        val drag = ItemTouchHelper.UP or ItemTouchHelper.DOWN
        val pos = h.layoutPosition
        val swipe = if (pos == _a._current) 0 else ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT // don't allow swipe current row
        return makeMovementFlags(drag, swipe)
    }
    // onMove will be called many many times if dragging toward top/bottom
    // to avoid this, make real swap at clearView which will be called only once
    private var _moveFrom = -1
    private var _moveTo = -1
    override fun onMove(rv: RecyclerView, h: RecyclerView.ViewHolder, dst: RecyclerView.ViewHolder): Boolean {
        if (_moveFrom == -1) {
            _moveFrom = h.layoutPosition
        }
        _moveTo = dst.layoutPosition
        return true
    }
    override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
        super.clearView(recyclerView, viewHolder)
        if (_moveFrom >= 0 && _moveTo >= 0 && _moveFrom != _moveTo) {
            _layers.removeAt(_moveFrom)
            _layers.add(_moveTo, layer)
            if (_moveFrom < _moveTo) { // drag down
                ...
            } else { // drag up
                 ...
            }
        }
        _moveFrom = -1
        _moveTo = -1
        Handler(Looper.getMainLooper()).post { _adapter.notifyDataSetChanged() }
    }
}
Related