androidandroid-recyclerviewitemtouchhelper

ItemTouchHelper.SimpleCallback onMove() is not triggered


I want to realize drag and drop in my RecyclerView. As I found out, the best way to implement this is by attaching an ItemTouchHelper to the RecyclerView.

onSwiped() works fine, but onMove() is never triggered.

This is what I do:

EditIngredientsRecyclerViewAdapter premixableIngredientsAdapter = new EditIngredientsRecyclerViewAdapter(this, typeOfComponents);
        GridLayoutManager gridLayoutManager1 = new GridLayoutManager(getContext(), 1);
        recyclerView.setAdapter(premixableIngredientsAdapter);
        recyclerView.setLayoutManager(gridLayoutManager1);

        ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT) {
            @Override
            public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
                Log.d(TAG, "onMove: ");
                return false;
            }

            @Override
            public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
                Log.d(TAG, "onSwiped: ");
            }
        };

        ItemTouchHelper touchHelper = new ItemTouchHelper(simpleItemTouchCallback);
        touchHelper.attachToRecyclerView(recyclerView);

Solution

  • I forgot two things:

    **1. update the SimpleCallback to this:

    ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT) {
                @Override
                public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
                    // Notify Adapter of the moved item!
                    recyclerView.getAdapter().notifyItemMoved(viewHolder.getAdapterPosition(), target.getAdapterPosition());
                    return true;
                }
    
                @Override
                public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
                    // No swipe action
                }
    
                @Override
                public boolean isItemViewSwipeEnabled() {
                    // Disable swipe (dont override this method or return true, if you want to have swipe)
                    return false;
                }
    
                @Override
                public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
                    // Set movement flags to specify the movement direction
                    // final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.RIGHT | ItemTouchHelper.LEFT;  <-- for all directions
                    // In this case only up and down is allowed
                    final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
                    final int swipeFlags = 0;
                    return makeMovementFlags(dragFlags, swipeFlags);
                }
            };
    

    Especially important is to overwrite getMovementFlags() to specify the direction for dragging and notify the adapter of the moved Item, as I do in onMove()

    2. Trigger dragging by itemTouchHelper.startDrag(viewHolderElement):

    I did this by creating a small interface in the RecyclerViewAdapter:

    public interface OnStartDragListener {
            void onStartDrag(RecyclerView.ViewHolder holder);
        }
    

    When creating the Adapter pass in a new OnStartDragListenerElement like so:

    EditIngredientsRecyclerViewAdapter premixableIngredientsAdapter = new EditIngredientsRecyclerViewAdapter(this, typeOfComponents, new EditIngredientsRecyclerViewAdapter.OnStartDragListener() {
                @Override
                public void onStartDrag(RecyclerView.ViewHolder holder) {
                    touchHelper.startDrag(holder);
                    Log.d(TAG, "onStartDrag: ");
                }
            });
    

    and calling onStartDrag(viewHolder) in an Touchlistener put on a button in the view like so:

    holder.itemBinding.dragBtn.setOnTouchListener(new View.OnTouchListener() {
                    @Override
                    public boolean onTouch(View v, MotionEvent event) {
                        mOnStartDragListener.onStartDrag(holder);
                        return false;
                    }
                });
    

    I am sure, there are more efficient implementations and once somebody posts one here, I will accept it. However, this at least made it working for me.