J'ai un problème avec ItemTouchHelper of RecyclerView .
Je fais un jeu. Le plateau de jeu est en réalité un RecyclerView. RecyclerView a GridLayoutManager avec un certain nombre d'étapes. Je veux implémenter les éléments de drag & drop recyclerview. Tout élément peut glisser dans toutes les directions (haut, bas, gauche, droite).
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, pourquoi la fonction onMoved de ItemTouchHelper fonctionne-t-elle lorsque je traîne toujours un élément sur le RecyclerView? Comment puis-je atteindre cet objectif ?
Lors du glisser-déposer d'un élément, onMove () peut être appelé plusieurs fois, mais clearView () sera appelé une fois. Vous pouvez donc utiliser ceci pour indiquer que la traînée était terminée (la chute a eu lieu). Et utilisez deux variables dragFrom et dragTo pour tracer la position réelle dans un "glisser-déposer" terminé.
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) était comme ci-dessous:
list.add(toPosition, list.remove(fromPosition));
notifyItemMoved(fromPosition, toPosition);
Le callback onSelectedChanged(RecyclerView.ViewHolder, int)
fournit des informations sur l’actionState en cours:
- ACTION_STATE_IDLE
:
- ACTION_STATE_DRAG
- ACTION_STATE_SWIPE
Ainsi, vous pouvez savoir si l'ordre a changé et lorsque l'état passe à ACTION_STATE_IDLE
, vous pouvez faire ce qu'il vous faut!
Exemple:
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;
}
}
Vous devez réaliser l'écouteur OnMove dans votre adaptateur:
Collections.swap(youCoolList, fromPosition, toPosition);
notifyItemMoved(fromPosition, toPosition);
comme cet homme fait https://medium.com/@ipaulpro/drag-and-swipe-with-recyclerview-b9456d2b1aaf#.blviq6jxp
exemple de grille spéciale https://medium.com/@ipaulpro/drag-and-swipe-with-recyclerview-6a6f0c422efd#.xb74uu7ke
J'ai fait quelques tests et onSelectedChanged(RecyclerView.ViewHolder?, Int)
m'a paru le plus fiable pour détecter la fin du geste (baisse). La méthode est appelée chaque fois qu'un élément est déplacé et que l'état d'action ACTION_STATE_DRAG
est transmis. Lorsque le glissement est terminé, il est appelé avec l'état d'action ACTION_STATE_IDLE
.
Voir ma solution ci-dessous. Le callback onItemDrag(Int, Int)
est utilisé pour réorganiser des éléments dans un adaptateur lors du déplacement de l'élément. D'autre part, le callback onItemDragged(Int, Int)
est destiné à la mise à jour des positions dans une base de données à la fin du geste.
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
}
}
}
}
}