Android RecyclerView drag and drop

Gopal Awasthi
3 min readNov 26, 2019

--

This is a basic example for drag and drop with Recycler View using ItemTouchHelper. Now lets understand what we want to do in this article. Drag and drop in recycler view is quite easy if we don’t have extra content to scroll rather than the list items, but the problems comes to start when we put the recycler view inside the NestedScrollView. So lets figure out what problems comes and how to solve them.

ItemTouchHelper

This is a utility class to add swipe to dismiss and drag & drop support to RecyclerView.

It works with a RecyclerView and a Callback class, which configures what type of interactions are enabled and also receives events when user performs these actions.

Depending on which functionality you support, you should override onMove(RecyclerView, ViewHolder, ViewHolder) and / or onSwiped(ViewHolder, int).

This class is designed to work with any LayoutManager but for certain situations, it can be optimized for your custom LayoutManager by extending methods in the ItemTouchHelper.Callback class or implementing ItemTouchHelper.ViewDropHandler interface in your LayoutManager.

ItemTouchHelper.Callback

This class is the contract between ItemTouchHelper and your application. It lets you control which touch behaviors are enabled per each ViewHolder and also receive callbacks when user performs these actions.

We must override some functions for using this callback:

getMovementFlags(RecyclerView, ViewHolder)onMove(RecyclerView, ViewHolder, ViewHolder)onSwiped(ViewHolder, int)

Now start implementing the logic for drag drop:

Initialize and set the adapter in the MainActivity:

recyclerView = findViewById(R.id.list);     
layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
adapter = new CustomAdapter(this, arrayList); recyclerView.setLayoutManager(layoutManager); recyclerView.setAdapter(adapter); recyclerView.setHasFixedSize(true);

Now if we put our recyclerview inside the nested scrollview as we want the that content to be move along with the list. So lets understand the problems occures one by one.

  1. If we put the recyclerview inside the nestedscrollview they does not scroll smoothly. So, we have one solution for that to set the nestedscrolling of the recyclerview to false i.e recyclerview. setNestedScrollingEnabled(false)
  2. This is a good solution if you don’t want to do drag drop and just want the list and other content to scroll smoothly. But if you now drag item out of visible space in recyclerview in nestedscrollview — nestedscrollView doesn’t scroll.

So now lets come to the solution. Firstly we remove the NestedScrollView which is the parent of the RecyclerView. As we know we can use other views as recyclerview. So lets move to the code:

We must override getItemViewType() method in the Adapter:

@Override public int getItemViewType(int position) {        if(position == 0){ 
return HEADERVIEW;
}
return LISTVIEW;
}

Now, we are adding the other view as the HeaderView in the list so we are going to tweak the list count:

@Override public int getItemCount() { 
return arrayList.size()+1;
}

Now lets write up the logic for onCreateViewHolder and onBindViewHolder.

In the onCreateViewHolder just return the view on the basis of the getItemViewType(). i.e

View v;     
if(getItemViewType(i) == HEADERVIEW){
// inflate the header view
return v;
}
// inflate the listview
return v;

Now bind the views correctly in onBindViewHolder on the basis on the getItemViewType().i.e

@Override public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int i) {        if(viewHolder.getItemViewType() == HEADERVIEW ){            HeaderViewHolder headerViewHolder = (HeaderViewHolder)viewHolder;
headerViewHolder.headertextview.SetText("LongText");
}else{
MyHolder myHolder = (MyHolder) viewHolder; myHolder.name.setText(arrayList.get(i-1).name);
}

Now, Adapter is setup completely. So lets dive into the drag drop logic:

  1. getMovementFlags- It defines the enabled move directions and to specify which direction drag and swipe supported.
@Override public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {                if(viewHolder.getItemViewType() == CustomAdapter.HEADERVIEW){                    return  makeMovementFlags(0,0); 
}
int dragflags = ItemTouchHelper.UP | ItemTouchHelper.DOWN; return makeMovementFlags(dragflags,0);
}

2. onMove- As Item will start dragging it will call on every position change during dragging.We have to tweak the logic for the onMove as we are also adding the headerview inside the RecyclerView and we do not want that view to be drag and drop as it is not a part of the list item.

public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { 
// get the viewHolder's and target's positions in your adapter data, swap them
if(viewHolder.getItemViewType() != target.getItemViewType()){ return false;
}
int fromPosition = viewHolder.getAdapterPosition(); int toPosition = target.getAdapterPosition(); if(dragFrom == -1) {
dragFrom = fromPosition;
}
dragTo = toPosition;
if(dragFrom != -1 && dragTo != -1 && dragFrom != dragTo) { reallyMoved(dragFrom, dragTo);
dragFrom = dragTo = -1;
}
// and notify the adapter that its dataset has changed
adapter.notifyItemMoved(viewHolder.getAdapterPosition(), target.getAdapterPosition());
return true;
}

Now if all the logic is right then we move the item and call onreallyMoved() method:

private void reallyMoved(int dragFrom, int dragTo) {                if(dragFrom == 0 || dragTo == arrayList.size()){                    return;
}
Collections.swap(arrayList, dragFrom-1, dragTo-1);
}

With our Callback ready, we can create our ItemTouchHelper and call attachToRecyclerView(RecyclerView)

ItemTouchHelper itemTouchHelper = new ItemTouchHelper(_ithCallback);        itemTouchHelper.attachToRecyclerView(recyclerView);

https://vimeo.com/375740962

You can find the complete code at Github

Hope it helps!!!!

Thanks

--

--