I have a problem with ItemTouchHelper of RecyclerView.
I am making a game. The game board is actually a RecyclerView. RecyclerView has GridLayoutManager with some span count. I want to implement drag & drop recyclerview's items. Any item can dragging over all directions (up, down, left, right).
private void initializeLayout() {
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutFrozen(true);
recyclerView.setNestedScrollingEnabled(false);
// set layout manager
GridLayoutManager layoutManager = new GridLayoutManager(getContext(), BOARD_SIZE,
LinearLayoutManager.VERTICAL, true);
recyclerView.setLayoutManager(layoutManager);
// Extend the Callback class
ItemTouchHelper.Callback itemTouchCallback = new ItemTouchHelper.Callback() {
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
Log.w(TAG, "onMove");
return false;
}
#Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
// Application does not include swipe feature.
}
#Override
public void onMoved(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder,
int fromPos, RecyclerView.ViewHolder target, int toPos, int x, int y) {
Log.d(TAG, "onMoved");
// this is calling every time, but I need only when user dropped item, not after every onMove function.
}
#Override
public boolean isItemViewSwipeEnabled() {
return false;
}
#Override
public boolean isLongPressDragEnabled() {
return true;
}
#Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.START | ItemTouchHelper.END;
int swipeFlags = 0;
return makeMovementFlags(dragFlags, swipeFlags);
}
};
ItemTouchHelper touchHelper = new ItemTouchHelper(itemTouchCallback);
touchHelper.attachToRecyclerView(recyclerView);
}
SO, why ItemTouchHelper's onMoved function works when I still dragging item on the RecyclerView ? How can I achieve this ?
While dragging and dropping an item, the onMove() can be called more than once, but the clearView() will be called once. So you can use this to indicate the drag was over(drop was happened).
And use two variables dragFrom and dragTo to trace the really position in a completed "drag & drop".
private ItemTouchHelper.Callback dragCallback = new ItemTouchHelper.Callback() {
int dragFrom = -1;
int dragTo = -1;
#Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
return makeMovementFlags(ItemTouchHelper.UP|ItemTouchHelper.DOWN|ItemTouchHelper.LEFT|ItemTouchHelper.RIGHT,
0);
}
#Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
int fromPosition = viewHolder.getAdapterPosition();
int toPosition = target.getAdapterPosition();
if(dragFrom == -1) {
dragFrom = fromPosition;
}
dragTo = toPosition;
adapter.onItemMove(fromPosition, toPosition);
return true;
}
private void reallyMoved(int from, int to) {
// I guessed this was what you want...
}
#Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
}
#Override
public boolean isLongPressDragEnabled() {
return true;
}
#Override
public boolean isItemViewSwipeEnabled() {
return false;
}
#Override
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder);
if(dragFrom != -1 && dragTo != -1 && dragFrom != dragTo) {
reallyMoved(dragFrom, dragTo);
}
dragFrom = dragTo = -1;
}
};
adapter.onItemMove(fromPosition, toPosition) was like below:
public void onItemMove(int fromPosition, int toPosition) {
list.add(toPosition, list.remove(fromPosition));
notifyItemMoved(fromPosition, toPosition);
}
The onSelectedChanged(RecyclerView.ViewHolder, int) callback provides information about the current actionState:
- ACTION_STATE_IDLE:
- ACTION_STATE_DRAG
- ACTION_STATE_SWIPE
So you could keep track whether the order changed, and when the state changes to ACTION_STATE_IDLE, you can do what you need to do!
Example:
private final class MyCallback extends ItemTouchHelper.Callback {
private boolean mOrderChanged;
#Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
// Check if positions of viewHolders correspond to underlying model, and if not, flip the items in the model and set the mOrderChanged flag
mOrderChanged = true;
}
#Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
super.onSelectedChanged(viewHolder, actionState);
if (actionState == ItemTouchHelper.ACTION_STATE_IDLE && mOrderChanged) {
doSomething();
mOrderChanged = false;
}
}
I did some tests and onSelectedChanged(RecyclerView.ViewHolder?, Int) seemed most reliable for me to detect end of the gesture (drop). The method is called whenever an item is being dragged and passed action state of ACTION_STATE_DRAG. When the drag is over, it is called with action state of ACTION_STATE_IDLE.
See my solution below. The onItemDrag(Int, Int) callback is used for reordering items in an adapter as the item is being dragged. On the other hand the onItemDragged(Int, Int) callback is intended for updating positions in a database at the end of the gesture.
class ItemGestureHelper(private val listener: OnItemGestureListener) : ItemTouchHelper.Callback() {
interface OnItemGestureListener {
fun onItemDrag(fromPosition: Int, toPosition: Int): Boolean
fun onItemDragged(fromPosition: Int, toPosition: Int)
fun onItemSwiped(position: Int)
}
private var dragFromPosition = -1
private var dragToPosition = -1
// Other methods omitted...
override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {
// Item is being dragged, keep the current target position
dragToPosition = target.adapterPosition
return listener.onItemDrag(viewHolder.adapterPosition, target.adapterPosition)
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
listener.onItemSwiped(viewHolder.adapterPosition)
}
override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
super.onSelectedChanged(viewHolder, actionState)
when (actionState) {
ItemTouchHelper.ACTION_STATE_DRAG -> {
viewHolder?.also { dragFromPosition = it.adapterPosition }
}
ItemTouchHelper.ACTION_STATE_IDLE -> {
if (dragFromPosition != -1 && dragToPosition != -1 && dragFromPosition != dragToPosition) {
// Item successfully dragged
listener.onItemDragged(dragFromPosition, dragToPosition)
// Reset drag positions
dragFromPosition = -1
dragToPosition = -1
}
}
}
}
}
You must implement OnMove listener in you adapter:
Collections.swap(youCoolList, fromPosition, toPosition);
notifyItemMoved(fromPosition, toPosition);
like this man doing
https://medium.com/#ipaulpro/drag-and-swipe-with-recyclerview-b9456d2b1aaf#.blviq6jxp
special grid example
https://medium.com/#ipaulpro/drag-and-swipe-with-recyclerview-6a6f0c422efd#.xb74uu7ke
Related
This is my implementation of ItemTouchHelper.callback
I implemented getMovementFlags so that it could drag and drop in UP, Down, left, and right directions but didn't execute the onMove method
I know where the problem is because I'm in the setOnLongClickListener notifyDataSetChanged on the item and that causes it to fail to drag but I do need to go through setOnLongClickListener, okay NotifyDataSetChanged updates the item to editable style
#Override
public int getMovementFlags(#NonNull RecyclerView recyclerView, #NonNull RecyclerView.ViewHolder viewHolder) {
int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
int swipeFlags = 0;
return makeMovementFlags(dragFlags, swipeFlags);
}
#Override
public boolean onMove(#NonNull RecyclerView recyclerView, #NonNull RecyclerView.ViewHolder viewHolder, #NonNull RecyclerView.ViewHolder target) {
int fromPosition = viewHolder.getAdapterPosition();
int toPosition = target.getAdapterPosition();
dragAdapter.itemMove(fromPosition, toPosition);
return true;
}
LEFT and RIGHT should be used for swipe, so keep UP and DOWN only for move position.
And, onMove will be called many many times, so you should implement clearView and do the move in clearView. onMove is just used to record positions.
private val _callback = object:ItemTouchHelper.Callback() {
override fun getMovementFlags(rv: RecyclerView, h: RecyclerView.ViewHolder): Int {
val drag = ItemTouchHelper.UP or ItemTouchHelper.DOWN
val pos = h.layoutPosition
val swipe = if (pos == _a._current) 0 else ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT // don't allow swipe current row
return makeMovementFlags(drag, swipe)
}
// onMove will be called many many times if dragging toward top/bottom
// to avoid this, make real swap at clearView which will be called only once
private var _moveFrom = -1
private var _moveTo = -1
override fun onMove(rv: RecyclerView, h: RecyclerView.ViewHolder, dst: RecyclerView.ViewHolder): Boolean {
if (_moveFrom == -1) {
_moveFrom = h.layoutPosition
}
_moveTo = dst.layoutPosition
return true
}
override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
super.clearView(recyclerView, viewHolder)
if (_moveFrom >= 0 && _moveTo >= 0 && _moveFrom != _moveTo) {
_layers.removeAt(_moveFrom)
_layers.add(_moveTo, layer)
if (_moveFrom < _moveTo) { // drag down
...
} else { // drag up
...
}
}
_moveFrom = -1
_moveTo = -1
Handler(Looper.getMainLooper()).post { _adapter.notifyDataSetChanged() }
}
}
I want to write a piece of code into where item drag-drop is over. I got this answer https://stackoverflow.com/a/36275415/1684778 but I am confused about how to achieve this(where i have to implement that answer)
I want to write onDrop instead of onItemMove, how to achieve it.
DragItemTouchHelper
public class DragItemTouchHelper extends ItemTouchHelper.Callback {
public static final float ALPHA_FULL = 1.0f;
private final MoveHelperAdapter mAdapter;
public DragItemTouchHelper(MoveHelperAdapter adapter) {
mAdapter = adapter;
}
#Override
public boolean isLongPressDragEnabled() {
return true;
}
#Override
public boolean isItemViewSwipeEnabled() {
return false;
}
#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) {
}
#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 TouchViewHolder) {
// Let the view holder know that this item is being moved or dragged
TouchViewHolder itemViewHolder = (TouchViewHolder) 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 TouchViewHolder) {
// Tell the view holder it's time to restore the idle state
TouchViewHolder itemViewHolder = (TouchViewHolder) viewHolder;
itemViewHolder.onItemClear();
}
}
public interface MoveHelperAdapter {
boolean onItemMove(int fromPosition, int toPosition);
}
public interface TouchViewHolder {
void onItemSelected();
void onItemClear();
}
}
You can put your class that extends ItemTouchHelper callback in the end of the activity class where your RecyclerView is and attach it to RecyclerView defined in activity or fragment as below
ItemTouchHelper touchHelper = new ItemTouchHelper(your_custom_callback);
touchHelper.attachToRecyclerView(recyclerView);
I am trying to implement ItemTouchHelper for Horizontal Recyclerview. (setting layout manager to LinearLayoutManager with orientation LinearLayoutManager.HORIZONTAL). Example, I want to delete an item when swiped down and drag to left or right for swapping items.
All the samples I have gone through explains ItemTouchHelper for Vertical Recyclerview or items in grid.
Followed samples from following links:
https://medium.com/#ipaulpro/drag-and-swipe-with-recyclerview-b9456d2b1aaf
https://medium.com/#xabaras/recyclerview-swiping-with-style-151e21b1af07
How can I achieve swipe down to delete and drag sideways to swap items in Horizontal Recyclerview?
You can use this simple code to achieve the swipe down to remove.
ItemTouchHelper.SimpleCallback simpleCallback = new ItemTouchHelper.SimpleCallback(0,ItemTouchHelper.DOWN) {
#Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
return false;
}
#Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
final int position = viewHolder.getLayoutPosition();
if (direction == ItemTouchHelper.DOWN) {
//your code for deleting the item from database or from the list
adapter.removeNote(position);
adapter.notifyItemRemoved(position)
}
}
};
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(simpleCallback);
itemTouchHelper.attachToRecyclerView(recyclerView);
For Kotlin geeks below is the code for the same -
val simpleCallback = object : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.DOWN) {
override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {
return false
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
if (direction == ItemTouchHelper.DOWN) {
//your code for deleting the item from database or from the list
val position = viewHolder.adapterPosition
noteList.removeAt(position)
adapter.notifyItemRemoved(position)
}
}
}
val itemTouchHelper = ItemTouchHelper(simpleCallback)
itemTouchHelper.attachToRecyclerView(recycler_view)
You only need to change the method "getMovementFlags".
#Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;
return makeMovementFlags(dragFlags, swipeFlags);
}
to
#Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
final int swipeFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
final int dragFlags = ItemTouchHelper.START | ItemTouchHelper.END;
return makeMovementFlags(dragFlags, swipeFlags);
}
To delete an item, create a new method in "ItemTouchHelperAdapter" called "onSwiped" and implement it to remove the item
#Override
public boolean onSwiped(int itemPosition, int direction) {
if(direction == SimpleItemTouchHelperCallback.SWIPED_TO_END) {
list.remove(itemPosition);
notifyItemRemoved(itemPosition);
}else{
notifyItemChanged(itemPosition);
}
return true;
}
I was able to implement drag and drop items in a list and reorder them. What I need to do is implement a delete functionality where one can select and hold an item and drag it to a trash icon. Once it overlaps the trash icon, the latter will change its color from grey to red. Once the user releases the item, a confirmation modal will appear.
From the demo, you can see that dragging the first three items in the list toward the trash icon did not turn the trash icon red but launched the modal. The fourth item did change the trash icon's color, but it was immediately removed from the list without confirmation.
The drag and drop is implemented using ItemTouchHelperCallback:
public class EditItemTouchHelperCallback extends ItemTouchHelper.Callback {
private final RVAdapter mAdapter;
private RecyclerView.ViewHolder curHolder;
private int curPos;
private boolean deletedSomething;
private CreatePhotostoryActivity cpa;
public EditItemTouchHelperCallback(RVAdapter adapter) {
mAdapter = adapter;
cpa = new CreatePhotostoryActivity();
}
#Override
public boolean isLongPressDragEnabled() {
return true;
}
#Override
public boolean isItemViewSwipeEnabled() {
return false;
}
#Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.START | ItemTouchHelper.END;
int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;
return makeMovementFlags(dragFlags, swipeFlags);
}
#Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder,
RecyclerView.ViewHolder target) {
curPos = target.getAdapterPosition();
if(!deletedSomething){
mAdapter.onItemMove(viewHolder.getAdapterPosition(), target.getAdapterPosition());
}
return true;
}
#Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
mAdapter.onItemDismiss(viewHolder.getAdapterPosition());
}
#Override
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
CreatePhotostoryActivity.trashNavbar.setVisibility(View.GONE);
CreatePhotostoryActivity.navbar.setVisibility(View.VISIBLE);
}
#Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
boolean isOver = false;
deletedSomething = false;
CreatePhotostoryActivity.trashNavbar.setVisibility(View.VISIBLE);
CreatePhotostoryActivity.trashNavbar.bringToFront();
CreatePhotostoryActivity.navbar.setVisibility(View.GONE);
if(ItemTouchHelper.ACTION_STATE_DRAG == actionState) {
if (isViewOverlapping(viewHolder.itemView, CreatePhotostoryActivity.trashNavbar)) {
CreatePhotostoryActivity.trashIcon.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.red500));
//delete moment
mAdapter.onItemDismiss(viewHolder.getAdapterPosition());
} else {
CreatePhotostoryActivity.trashIcon.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.grey700));
}
curHolder=viewHolder;
curPos = curHolder.getAdapterPosition();
Log.v("Selected","a viewholder "+ curPos);
}
if (isViewOverlapping(curHolder.itemView, CreatePhotostoryActivity.trashNavbar)) {
isOver = true;
}
if(isOver) {
deletedSomething = true;
mAdapter.showConfirmationDialogDeleteMoment(curPos);
Log.v("Dropped","a viewholder "+curHolder);
}
}
private boolean isViewOverlapping(View firstView, View secondView) {
int[] firstPosition = new int[2];
int[] secondPosition = new int[2];
firstView.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
firstView.getLocationOnScreen(firstPosition);
secondView.getLocationOnScreen(secondPosition);
int r = firstView.getMeasuredHeight() + firstPosition[1];
int l = secondPosition[1];
return r >= l && (r != 0 && l != 0);
}
}
As you can see it's onSelectedChanged that sets the color of the trash icon and calls the method to launch the confirmation modal. I'm not really sure if I placed the methods in the correct position. In my adapter, I use the following method to delete the item from the list:
#Override
public void onItemDismiss(int position) {
moments.remove(position);
notifyItemRemoved(position);
}
The method to launch the confirmation modal calls the above method. It is also located in my adapter:
public void showConfirmationDialogDeleteMoment(final int position) {
AlertDialog.Builder builder = new AlertDialog.Builder(createPhotoStoryLayout.getContext(), R.style.AlertDialog);
builder.setTitle("DO YOU WANT TO DELETE THIS MOMENT?")
.setPositiveButton("DELETE", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
onItemDismiss(position);
}
})
.setNegativeButton("NO", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
//do nothing
}
})
.setIcon(0);
AlertDialog alert = builder.create();
alert.show();
alert.getWindow().setLayout(WindowManager.LayoutParams.MATCH_PARENT,
(int) (170 * Resources.getSystem().getDisplayMetrics().density));
AlertUtils.displayDialog(createPhotoStoryLayout.getContext(), alert, R.drawable.red_500_pill, R.drawable.grey_700_pill, R.color.red500, R.color.grey700);
}
Basically, what I want is something similar to the Android OS, where a user drags the app to the Uninstall icon, changes the icon's color to red, and deletes the app once it's released.
For developers that still looking for a solution; just an idea, you can try below code. A part of my ItemTouchHelper.Callback implementation. I tried and its working fine in my case.
interface Callback{
fun isInDeleteArea(dX: Float, dY: Float): Boolean
}
private var removeItem = false
lateinit var callback: Callback
override fun onChildDraw(
c: Canvas,
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
dX: Float,
dY: Float,
actionState: Int,
isCurrentlyActive: Boolean
) {
if (isCurrentlyActive.not() && callback.isInDeleteArea(dX, dY)) {
removeItem = true
viewHolder.itemView.isGone = true
return
}
viewHolder.itemView.translationY = dY
viewHolder.itemView.translationX = dX
}
override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
}
override fun getAnimationDuration(
recyclerView: RecyclerView,
animationType: Int,
animateDx: Float,
animateDy: Float
): Long {
if (removeItem) return 0
return super.getAnimationDuration(recyclerView, animationType, animateDx, animateDy)
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {}
override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
removeItem = false
super.onSelectedChanged(viewHolder, actionState)
}
public class ItemCallBack extends ItemTouchHelper.Callback {
private BaseDragAdapter adapter;
private List list;
public ItemCallBack(BaseDragAdapter adapter, List list) {
this.adapter = adapter;
this.list = list;
}
#Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
int dragFlags = 0;
int swipeFlags = 0;
int startPosition = viewHolder.getAdapterPosition();
if (startPosition != 0) {
if (recyclerView.getLayoutManager() instanceof GridLayoutManager) {
dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
} else if (recyclerView.getLayoutManager() instanceof LinearLayoutManager) {
dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
} else if (recyclerView.getLayoutManager() instanceof StaggeredGridLayoutManager) {
dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
}
}
return makeMovementFlags(dragFlags, swipeFlags);
}
#Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
int startPosition = viewHolder.getAdapterPosition();
int targetPosition = target.getAdapterPosition();
adapter.onMoved(startPosition, targetPosition);
return true;
}
#Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { //左右拖动删除
int position = viewHolder.getAdapterPosition();
adapter.onSwiped(position);
}
#Override
public boolean isLongPressDragEnabled() {
return true;
}
#Override
public boolean isItemViewSwipeEnabled() {
return false;
}
#Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
viewHolder.itemView.setScaleX(1.2f);
viewHolder.itemView.setScaleY(1.2f);
}
super.onSelectedChanged(viewHolder, actionState);
}
#Override
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder);
viewHolder.itemView.setScaleX(1.0f);
viewHolder.itemView.setScaleY(1.0f);
onOrderListener.order();
}
#Override
public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
}
private OnOrderListener onOrderListener;
public void setOnPictureClickListener(OnOrderListener onOrderListener) {
if (onOrderListener != null) {
this.onOrderListener = onOrderListener;
}
}
public interface OnOrderListener {
void order();
}
}
#Override
public int getItemViewType(int position) {
if (position == 0) {
return TYPE_ADD;
} else if (position == 1) {
return TYPE_COVER;
}
return TYPE_NORMAL;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == TYPE_ADD) {
View view = LayoutInflater.from(context).inflate(R.layout.item_upload_pic_add, null);
return new AddHolder(view);
} else if (viewType == TYPE_COVER) {
View view = LayoutInflater.from(context).inflate(R.layout.item_upload_pic_cover, null);
return new CoverHolder(view);
} else {
View view = LayoutInflater.from(context).inflate(R.layout.item_upload_pic_normal, null);
return new NormalHolder(view);
}
}
I wanna swap item with ItemTouchHelper.
The issue is:
When i drag and swap item that viewtype is TYPE_NORNAL,animation work well;
But When i try to switch between different types(ex:TYPE_NORMAL and TYPE_COVER),animation does not execute correctly.
How to make the animation execute correctly?
I had the same issue. Resoved it with setting
setHasStableIds(true);
on adapter, and overriding getItemId() so that it returns unique id for each item I have in adapter. If you do not have unique id for items you can spawn Map and generate these id's when you get new items for adapter. Then lookup these id's in getItemId(), using item as a key.
Override getMovementFlags and set your swipeFlags depends of your viewType
override fun getMovementFlags(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder
): Int {
val swipeFlags = if (viewHolder.itemViewType == ListItem.TYPE_BODY)
ItemTouchHelper.RIGHT
else 0
return makeMovementFlags(0, swipeFlags)
}