Hello Friends,
This tutorial will help you to implement "drag & drop" and "swipe" functionality with Recyclerview items. There are many different ways to implement this features but we are using really a simple way to add these features in Recyclerview. It’s already part of the Android Support Library and needs one class. This class is called ItemTouchHelper.
ItemTouchHelper :- ItemTouchHelper is a powerful class that can do everything you need to drag, drop, swipe in RecyclerView. It’s a subclass of RecyclerView.ItemDecoration, that mean it's easily added to existing LinearLayout Manager and Adapter.
We can implement this feature by following steps.
Step-1: First we’ll create an interface ItemTouchHelperAdapter that allows us to pass two events onMove() and onSwiped() event callbacks back up the chain.
public interface ItemTouchHelperAdapter {
void onItemMove(int fromPosition, int toPosition);
void onItemDismiss(int position);
}
This interface will implement by RecyclerViewAdapter class as listener
Step-2: Defined here complete code for RecyclerView Adapter which implement ItemTouchHelperAdapter interface
public class RecylerViewAdapter extends RecyclerView.Adapter<RecylerViewAdapter.ViewHolder> implements ItemTouchHelperAdapter {
private ArrayList<String> list;
private final OnStartDragListener mDragStartListener;
public RecylerViewAdapter(ArrayList<String> list, Context context, OnStartDragListener dragStartListener) {
this.list = list;
mDragStartListener = dragStartListener;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_layout, parent, false);
return new ViewHolder(v);
}
@Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
holder.textView.setText(list.get(position));
holder.imageView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_DOWN) {
mDragStartListener.onStartDrag(holder);
}
return false;
}
});
}
@Override
public int getItemCount() {
return list.size();
}
@Override
public boolean onItemMove(int fromPosition, int toPosition) {
Collections.swap(list, fromPosition, toPosition);
notifyItemMoved(fromPosition, toPosition);
return true;
}
@Override
public void onItemDismiss(int position) {
list.remove(position);
notifyItemRemoved(position);
}
public class ViewHolder extends RecyclerView.ViewHolder implements
ItemTouchHelperViewHolder {
TextView textView;
ImageView imageView;
public ViewHolder(View itemView) {
super(itemView);
textView=(TextView)itemView.findViewById(R.id.text);
imageView=(ImageView)itemView.findViewById(R.id.handle);
}
@Override
public void onItemSelected() {
itemView.setBackgroundColor(Color.LTGRAY);
}
@Override
public void onItemClear() {
itemView.setBackgroundColor(0);
}
}
}
Here it is very necessary to call notifyItemRemoved() and notifyItemMoved() so that Adapter will get aware when any changes happen.
Step-3: Defined another interface ItemTouchHelperViewHolder and implement by ViewHolder class.
This interface holds two callbacks events. This callback call when you selected any item from RecyclerView.
public interface ItemTouchHelperViewHolder {
/ * Implementations should update the item view to indicate it's active state. */
void onItemSelected();
/*
Called when completed the move or swipe, and the active item * state should be cleared.
*/
void onItemClear();
}
Step-4: Defined interface OnStartDragListener .
**
* Listener for manual initiation of a drag.
*/
public interface OnStartDragListener {
/**
* Called when a view is requesting a start of a drag.
*/
void onStartDrag(RecyclerView.ViewHolder viewHolder);
}
Finally, We will define resulting call back class SimpleItemTouchHelperCallback
public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback {
public static final float ALPHA_FULL = 1.0f;
private final ItemTouchHelperAdapter mAdapter;
public SimpleItemTouchHelperCallback(ItemTouchHelperAdapter adapter) {
mAdapter = adapter;
}
@Override
public boolean isLongPressDragEnabled() {
return true;
}
@Override
public boolean isItemViewSwipeEnabled() {
return true;
}
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
// Set movement flags based on the layout manager
if (recyclerView.getLayoutManager() instanceof GridLayoutManager) {
final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
final int swipeFlags = 0;
return makeMovementFlags(dragFlags, swipeFlags);
} else {
final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
final int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;
return makeMovementFlags(dragFlags, swipeFlags);
}
}
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder source, RecyclerView.ViewHolder target) {
if (source.getItemViewType() != target.getItemViewType()) {
return false;
}
// Notify the adapter of the move
mAdapter.onItemMove(source.getAdapterPosition(), target.getAdapterPosition());
return true;
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int i) {
// Notify the adapter of the dismissal
mAdapter.onItemDismiss(viewHolder.getAdapterPosition());
}
@Override
public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
// Fade out the view as it is swiped out of the parent's bounds
final float alpha = ALPHA_FULL - Math.abs(dX) / (float) viewHolder.itemView.getWidth();
viewHolder.itemView.setAlpha(alpha);
viewHolder.itemView.setTranslationX(dX);
} else {
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
}
}
@Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
// We only want the active item to change
if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
if (viewHolder instanceof ItemTouchHelperViewHolder) {
// Let the view holder know that this item is being moved or dragged
ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperViewHolder) viewHolder;
itemViewHolder.onItemSelected();
}
}
super.onSelectedChanged(viewHolder, actionState);
}
@Override
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder);
viewHolder.itemView.setAlpha(ALPHA_FULL);
if (viewHolder instanceof ItemTouchHelperViewHolder) {
// Tell the view holder it's time to restore the idle state
ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperViewHolder) viewHolder;
itemViewHolder.onItemClear();
}
}
}
Step-5 : With our Callback ready, we can create our ItemTouchHelper and call attachToRecyclerView(RecyclerView) in MainActivity class and implement interface onStartDragListener.
ItemTouchHelper.Callback callback = new SimpleItemTouchHelperCallback(adapter);
ItemTouchHelper touchHelper = new ItemTouchHelper(callback);
touchHelper.attachToRecyclerView(recyclerView);
@Override
public void onStartDrag(RecyclerView.ViewHolder viewHolder) {
mItemTouchHelper.startDrag(viewHolder);
}
Step-6: Create a XML file for the items in RecyclerView item_layout.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/item"
android:layout_width="match_parent"
android:layout_height="?listPreferredItemHeight"
android:clickable="true"
android:focusable="true"
android:foreground="?selectableItemBackground">
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="@dimen/activity_horizontal_margin"
android:textAppearance="?android:attr/textAppearanceMedium" />
<ImageView
android:id="@+id/handle"
android:layout_width="?listPreferredItemHeight"
android:layout_height="match_parent"
android:layout_gravity="center_vertical|right"
android:scaleType="center"
android:src="@drawable/ic_reorder_grey_500_24dp" />
</FrameLayout>
2 Comment(s)