I have ItemTouchHelper class that uses swiping and drag and drop for performing actions. But i want to change drag and drop behavior. It should swap positions of 2 elements, the first one I dragged and the other one where it is dropped on. i want to exchange the items of the dragged & dropped positions. not to change positions of all items among both of them.
How to do it
this is my class for drag and drop
public class ItemTouchHelper extends
androidx.recyclerview.widget.ItemTouchHelper.Callback {
private Drawable icon;
private Context context;
private ColorDrawable background;
private final ItemTouchHelperListener dragDropListener;
public ItemTouchHelper(Context context, Drawable icon,
ItemTouchHelperListener dragDropListener) {
this.icon = icon;
this.dragDropListener = dragDropListener;
this.context = context;
this.background = new ColorDrawable(context.getResources().getColor(R.color.deleteItem));
}
@Override
public void onChildDraw(@NonNull Canvas c, @NonNull RecyclerView recyclerView,
@NonNull RecyclerView.ViewHolder viewHolder,
float dX, float dY,
int actionState, boolean isCurrentlyActive) {
super.onChildDraw(c, recyclerView, viewHolder, dX,
dY, actionState, isCurrentlyActive);
View itemView = viewHolder.itemView;
int iconMargin = (itemView.getHeight() - icon.getIntrinsicHeight()) / 2;
int iconTop = itemView.getTop() + (itemView.getHeight() - icon.getIntrinsicHeight()) / 2;
int iconBottom = iconTop + icon.getIntrinsicHeight();
if (dX > 0) {
background = new ColorDrawable(context.getResources().getColor(R.color.deleteItem));
iconMargin = (itemView.getHeight() - icon.getIntrinsicHeight()) / 2;
iconTop = itemView.getTop() + (itemView.getHeight() - icon.getIntrinsicHeight()) / 2;
iconBottom = iconTop + icon.getIntrinsicHeight();
int iconRight = itemView.getLeft() + iconMargin + icon.getIntrinsicWidth();
int iconLeft = itemView.getLeft() + iconMargin;
icon.setBounds(iconLeft, iconTop, iconRight, iconBottom);
background.setBounds(itemView.getLeft(), itemView.getTop(),
itemView.getLeft() + ((int) dX),
itemView.getBottom());
} else if (dX < 0) {
int iconRight = itemView.getRight() - iconMargin;
int iconLeft = itemView.getRight() - iconMargin - icon.getIntrinsicWidth();
icon.setBounds(iconLeft, iconTop, iconRight, iconBottom);
background.setBounds(itemView.getRight(), itemView.getTop(),
itemView.getRight() + ((int) dX),
itemView.getBottom());
} else {
background.setBounds(0, 0, 0, 0);
icon.setBounds(0, 0, 0, 0);
}
background.draw(c);
icon.draw(c);
}
@Override
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder,
int direction) {
dragDropListener.deleteElementDialog(viewHolder.getAdapterPosition());
}
@Override
public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
int dragFlags = UP | DOWN;
int swipeFlags = START | END;
return makeMovementFlags(dragFlags, swipeFlags);
}
@Override
public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder,
@NonNull RecyclerView.ViewHolder target) {
dragDropListener.onRowMoved(viewHolder.getAdapterPosition(), target.getAdapterPosition());
return true;
}
@Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder,
int actionState) {
if (actionState != ACTION_STATE_IDLE && actionState != ACTION_STATE_SWIPE) {
dragDropListener.onRowSelected(viewHolder);
}
super.onSelectedChanged(viewHolder, actionState);
}
@Override
public void clearView(@NonNull RecyclerView recyclerView,
@NonNull RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, Objects.requireNonNull(viewHolder));
dragDropListener.onRowClear(Objects.requireNonNull(viewHolder));
}
@Override
public boolean isLongPressDragEnabled() {
return true;
}
}
and this is my ItemTouchHelperListener:
public void setItemTouchHelperListener() {
ItemTouchHelperListener itemTouchHelperListener = new
ItemTouchHelperListener() {
@Override
public void onRowMoved(int fromPosition, int toPosition) {
presenter.rowMoved(fromPosition, toPosition);
}
@Override
public void onRowSelected(RecyclerView.ViewHolder myViewHolder) {
if (myViewHolder instanceof ElementsAdapter.ElementsViewHolder) {
elementsAdapter.rowSelected((ElementsAdapter.ElementsViewHolder) myViewHolder);
presenter.rowSelected(myViewHolder.getAdapterPosition());
}
}
@Override
public void onRowClear(RecyclerView.ViewHolder myViewHolder) {
if (myViewHolder instanceof ElementsAdapter.ElementsViewHolder) {
elementsAdapter.rowClear((ElementsAdapter.ElementsViewHolder) myViewHolder);
presenter.rowClear(myViewHolder.getAdapterPosition());
}
}
@Override
public void deleteElementDialog(int adapterPosition) {
createDeleteDialog(adapterPosition);
}
};
You can achieve this by registering both the dragged item using onMove() method, and the dropped item using clearView() method; then modify the data source of your RecyclerView
adapter; so you can use a temp item that stores the dragged item; then set the dropped-by item with the dragged one; and finally put the temp item on the dropped one.
Then utilize RecyclerView
adapter notifyItemChanged()
for both items to update the layout with this change
Note: here I disabled the swiping as your question mainly on the drag & drop
final int[] oldPos = new int[1];
final int[] newPos = new int[1];
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(
ItemTouchHelper.UP |
ItemTouchHelper.DOWN |
ItemTouchHelper.LEFT |
ItemTouchHelper.RIGHT,
0) {
@Override
public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
oldPos[0] = viewHolder.getAdapterPosition();
newPos[0] = target.getAdapterPosition();
return false;
}
@Override
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
}
@Override
public void clearView(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder);
moveItem(oldPos[0], newPos[0]);
}
});
private void moveItem(int oldPos, int newPos) {
Item temp = mItems.get(oldPos);
mItems.set(oldPos, mItems.get(newPos));
mItems.set(newPos, temp);
mAdapter.notifyItemChanged(oldPos);
mAdapter.notifyItemChanged(newPos);
}
The result