recyclerview recycleritemtouchhelper delete and adding android drag-and-drop android-recyclerview gridlayoutmanager

recycleritemtouchhelper - swipe menu recyclerview android



Android: cómo capturar la acción Drop de ItemTouchHelper que se usa con RecyclerView (4)

Debe realizar la escucha OnMove en su adaptador:

Collections.swap(youCoolList, fromPosition, toPosition); notifyItemMoved(fromPosition, toPosition);

como este hombre haciendo https://medium.com/@ipaulpro/drag-and-swipe-with-recyclerview-b9456d2b1aaf#.blviq6jxp

ejemplo de cuadrícula especial https://medium.com/@ipaulpro/drag-and-swipe-with-recyclerview-6a6f0c422efd#.xb74uu7ke

Tengo un problema con ItemTouchHelper de RecyclerView .

Estoy haciendo un juego. El tablero de juego es en realidad un RecyclerView. RecyclerView tiene GridLayoutManager con algunos recuentos de intervalo. Quiero implementar los elementos de arrastrar y soltar recyclerview. Cualquier elemento puede arrastrar sobre todas las direcciones (arriba, abajo, izquierda, derecha).

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); }

Entonces, ¿por qué la función onMoved de ItemTouchHelper funciona cuando sigo arrastrando un elemento en RecyclerView? Cómo puedo conseguir esto ?


Hice algunas pruebas y onSelectedChanged(RecyclerView.ViewHolder?, Int) pareció más confiable para detectar el final del gesto (soltar). Se llama al método siempre que se arrastra un elemento y se pasa el estado de acción de ACTION_STATE_DRAG . Cuando finaliza el arrastre, se llama con el estado de acción de ACTION_STATE_IDLE .

Vea mi solución a continuación. La devolución de llamada onItemDrag(Int, Int) se utiliza para reordenar los elementos en un adaptador a medida que se arrastra el elemento. Por otro lado, la devolución de llamada onItemDragged(Int, Int) está destinada a actualizar las posiciones en una base de datos al final del gesto.

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 } } } }

}


La devolución de llamada onSelectedChanged(RecyclerView.ViewHolder, int) proporciona información sobre el estado de acción actual:
- ACTION_STATE_IDLE :
- ACTION_STATE_DRAG
- ACTION_STATE_SWIPE

Por lo tanto, puede hacer un seguimiento de si el orden cambió y, cuando el estado cambie a ACTION_STATE_IDLE , ¡puede hacer lo que necesita hacer!

Ejemplo:

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; } }


Mientras arrastra y suelta un elemento, se puede llamar a onMove () más de una vez, pero se debe llamar a clearView () una vez. Por lo tanto, puede usar esto para indicar que el arrastre había terminado (se realizó la caída). Y use dos variables, dragFrom y dragTo para rastrear la posición real en un completo "arrastrar y soltar".

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) fue como a continuación:

list.add(toPosition, list.remove(fromPosition)); notifyItemMoved(fromPosition, toPosition);