The drag-and-drop sorting of RecyclerView requires the use of the ItemTouchHelper class. The ItemTouchHelper class is a utility class provided by Google that supports swipe and drag actions for RecyclerView. Below, we will implement the swipe-to-delete and drag-and-drop sorting functionalities using this class.
First, let's briefly introduce an internal abstract class of ItemTouchHelper called Callback.
ItemTouchHelper.Callback#
This class is a static abstract class within the ItemTouchHelper class, primarily serving to link ItemTouchHelper with your application, allowing developers to control the specific behavior of each View through ViewHolder and receive user event callbacks. This class contains three abstract methods: getMovementFlags, onMove, and onSwiped, which are frequently used in development.
getMovementFlags#
This method returns a Flags indicating the three states of an Item: idle, swiping, and dragging. Depending on the different layout managers of RecyclerView, it sets different swipe and drag directions, generally returning via the makeMovementFlags(int dragFlags, int swipeFlags) method, where dragFlags indicates the drag direction and swipeFlags indicates the swipe direction.
public abstract int getMovementFlags(RecyclerView recyclerView, ViewHolder viewHolder);
onMove#
This method will be called when ItemTouchHelper drags an Item, moving it from its old position to a new position. If not dragged, this method will never be called. Returning true indicates that the Item has been moved to a new position.
public abstract boolean onMove(RecyclerView recyclerView, ViewHolder viewHolder, ViewHolder target);
onSwiped#
This method is called when an Item is swiped. If not swiped, this method will not be called. You can use the direction to perform certain operations accordingly.
public abstract void onSwiped(ViewHolder viewHolder, int direction);
Additionally, commonly used methods include onSelectedChanged, clearView, etc.
onSelectedChanged#
This method is called when an item changes from an idle state to a swiping or dragging state. You can use actionState to determine the state of the Item and perform certain operations. When overriding this method, you must call the superclass's method.
public void onSelectedChanged(ViewHolder viewHolder, int actionState) {
if (viewHolder != null) {
sUICallback.onSelected(viewHolder.itemView);
}
}
clearView#
This method is called when user interaction ends or related animations complete.
public void clearView(RecyclerView recyclerView, ViewHolder viewHolder) {
sUICallback.clearView(viewHolder.itemView);
}
Implementing Drag-and-Drop Sorting in RecyclerView#
The drag-and-drop sorting of RecyclerView requires the use of the android.support.v7.widget.helper.ItemTouchHelper class. The key focus for drag-and-drop sorting is the onMove(int fromPosition, int toPosition) method in the interface, which is implemented in GridAdapter as follows:
@Override
public void onMove(int fromPosition, int toPosition) {
if (fromPosition < toPosition) {
for (int i = fromPosition; i < toPosition; i++) {
Collections.swap(list, i, i + 1);
}
} else {
for (int i = fromPosition; i > toPosition; i--) {
Collections.swap(list, i, i - 1);
}
}
notifyItemMoved(fromPosition, toPosition);
}
Implementing Swipe-to-Delete in RecyclerView#
The drag-and-drop sorting of RecyclerView requires the use of the android.support.v7.widget.helper.ItemTouchHelper class. The key focus for swipe-to-delete is the onSwiped(int position) method in the interface, which is implemented in GridAdapter as follows:
@Override
public void onSwiped(int position) {
Log.i("drag","onSwiped");
list.remove(position);
notifyItemRemoved(position);
}
Reference Code#
Implementation of ItemTouchHelper.Callback#
/**
* Created by jzman on 2017/5/17 0015.
*/
public class ItemTouchCallBack extends ItemTouchHelper.Callback {
private static final String TAG = "drag";
private OnItemTouchListener onItemTouchListener;
public void setOnItemTouchListener(OnItemTouchListener onItemTouchListener) {
this.onItemTouchListener = onItemTouchListener;
}
/**
* Set different swipe and drag directions based on the different layout managers of RecyclerView.
* This method uses makeMovementFlags(int dragFlags, int swipeFlags) to return.
* Parameters: dragFlags: drag direction
* swipeFlags: swipe direction
* @param recyclerView
* @param viewHolder
* @return
*/
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
Log.i(TAG,"getMovementFlags");
if (recyclerView.getLayoutManager() instanceof GridLayoutManager ||
recyclerView.getLayoutManager() instanceof StaggeredGridLayoutManager){
// No need for swipe operations here, can set to an integer other than 4 and 8, set to 0 here
// No swipe support
return makeMovementFlags(ItemTouchHelper.UP | ItemTouchHelper.DOWN |
ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT, 0 );
}else {
// If it's LinearLayoutManager, can only swipe up and down,
// here the second parameter is set to support swiping to the right
return makeMovementFlags(ItemTouchHelper.UP | ItemTouchHelper.DOWN , ItemTouchHelper.RIGHT );
}
}
/**
* This method will be called when ItemTouchHelper drags an Item, moving it from its old position to a new position.
* If not dragged, this method will never be called. Returning true indicates that it has been moved to a new position.
* @param recyclerView
* @param viewHolder
* @param target
* @return
*/
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
Log.i(TAG,"onMove");
int fromPosition = viewHolder.getAdapterPosition();
int toPosition = target.getAdapterPosition();
onItemTouchListener.onMove(fromPosition,toPosition);
return true;
}
/**
* Called when an Item is swiped.
* If you do not swipe, this method will not be called.
* @param viewHolder
* @param direction
*/
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
Log.i(TAG,"onSwiped");
// This is the main code for swipe-to-delete
int position = viewHolder.getAdapterPosition();
onItemTouchListener.onSwiped(position);
}
/**
* Called when an Item is swiped or dragged.
* @param viewHolder
* @param actionState
*/
@Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
Log.i(TAG,"onSelectedChanged");
//...
super.onSelectedChanged(viewHolder, actionState);
}
/**
* Called when user interaction ends or related animations complete.
* @param recyclerView
* @param viewHolder
*/
@Override
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
Log.i(TAG,"clearView");
//...
super.clearView(recyclerView, viewHolder);
}
/**
* Listener for updating data during move and swap.
*/
public interface OnItemTouchListener {
// Called when dragging an Item
void onMove(int fromPosition, int toPosition);
// Called when swiping an Item
void onSwiped(int position);
}
}
Implementation of Adapter#
/**
* Created by jzman on 2017/05/17 0009.
* RecyclerView Adapter
*/
public class GridAdapter extends RecyclerView.Adapter<GridAdapter.DataViewHolder> implements View.OnClickListener,ItemTouchCallBack.OnItemTouchListener {
private Context context;
private List<SimpleTitleGrid> list;
public GridAdapter(Context context, List<SimpleTitleGrid> list) {
this.context = context;
this.list = list;
}
/**
* Create ViewHolder
* @param parent
* @param viewType
* @return
*/
@Override
public DataViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// Load item layout file (each one)
View view = LayoutInflater.from(context).inflate(R.layout.item,null);
// Set click event for the View
view.setOnClickListener(this);
DataViewHolder viewHolder = new DataViewHolder(view);
// Set width and height programmatically (when XML layout settings are ineffective)
view.setLayoutParams(new RecyclerView.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
return viewHolder;
}
/**
* Bind data
* @param holder
* @param position
*/
@Override
public void onBindViewHolder(DataViewHolder holder, int position) {
// Set the height of each Item
holder.textView.setText(list.get(position).getTitle());
}
/**
* Total number of items
* @return
*/
@Override
public int getItemCount() {
return list.size();
}
/**
* Click event
* @param v
*/
@Override
public void onClick(View v) {
if(onItemClickListener!=null){
int position = recyclerView.getChildAdapterPosition(v);
// When the program reaches here, the specific implementation of this method will be executed
onItemClickListener.onItemClick(recyclerView,v,position,list.get(position));
}
}
@Override
public void onMove(int fromPosition, int toPosition) {
if (fromPosition < toPosition) {
for (int i = fromPosition; i < toPosition; i++) {
Collections.swap(list, i, i + 1);
}
} else {
for (int i = fromPosition; i > toPosition; i--) {
Collections.swap(list, i, i - 1);
}
}
notifyItemMoved(fromPosition, toPosition);
}
@Override
public void onSwiped(int position) {
Log.i("drag","onSwiped");
list.remove(position);
notifyItemRemoved(position);
}
/**
* Implementation for ViewHolder in RecyclerView
*/
public static class DataViewHolder extends RecyclerView.ViewHolder {
TextView textView;
public DataViewHolder(View itemView) {
super(itemView);
textView = (TextView) itemView.findViewById(R.id.tv_grid);
}
}
private RecyclerView recyclerView;
private OnItemClickListener onItemClickListener;
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
this.onItemClickListener = onItemClickListener;
}
/**
* Design a callback interface for item click events in RecyclerView (for external use)
* Reference ListView item click event method
*/
public interface OnItemClickListener{
// Parameters (parent component, clicked View, position, which could be an object's id or object/ not needed here)
void onItemClick(RecyclerView recyclerView, View view, int position, SimpleTitleGrid obj);
}
/**
* Attach RecyclerView to Adapter
*/
@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
this.recyclerView= recyclerView;
}
/**
* Detach RecyclerView from Adapter
*/
@Override
public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
super.onDetachedFromRecyclerView(recyclerView);
this.recyclerView = null;
}
}
MainActivity#
/**
* Created by jzman on 2017/05/17 0029.
* RecyclerView Adapter
*/
public class MainActivity extends AppCompatActivity implements GridAdapter.OnItemClickListener{
private RecyclerView rv_user;
private GridAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
rv_user = (RecyclerView) findViewById(R.id.rv_user);
adapter = new GridAdapter(this, DataUtils.getUserGrids());
ItemTouchCallBack touchCallBack = new ItemTouchCallBack();
touchCallBack.setOnItemTouchListener(adapter);
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(touchCallBack);
rv_user.setLayoutManager(new GridLayoutManager(this,3));
// rv_user.setLayoutManager(new LinearLayoutManager(this));
rv_user.setAdapter(adapter);
itemTouchHelper.attachToRecyclerView(rv_user);
adapter.setOnItemClickListener(this);
}
@Override
public void onItemClick(RecyclerView recyclerView, View view, int position, SimpleTitleGrid obj) {
Toast.makeText(MainActivity.this, obj.getTitle(), Toast.LENGTH_SHORT).show();
}
}
Display Effects#
GridLayoutManager | LinearLayoutManager |
---|---|
The implementation of drag-and-drop sorting and swipe-to-delete functionality in RecyclerView concludes here.