Android - как поймать действие Drop ItemTouchHelper, которое используется с RecyclerView
У меня проблема с ItemTouchHelper RecyclerView.
Я делаю игру. Игровая доска - это, фактически, RecyclerView. RecyclerView имеет GridLayoutManager с некоторым количеством периодов. Я хочу реализовать перетаскивание элементов recyclerview. Любой элемент может перемещаться по всем направлениям (вверх, вниз, влево, вправо).
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, почему функция ItemTouchHelper onMoved работает, когда я все еще перетаскиваю элемент в RecyclerView? Как я могу достичь этого?
Ответы
Ответ 1
При перетаскивании элемента onMove() может вызываться более одного раза, но clearView() вызывается один раз. Таким образом, вы можете использовать это, чтобы указать, что перетаскивание закончилось (падение произошло). И используйте две переменные dragFrom и dragTo, чтобы отследить реальную позицию в завершенном "перетаскивании".
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) был похож на ниже:
list.add(toPosition, list.remove(fromPosition)); notifyItemMoved(fromPosition, toPosition);
Ответ 2
Обратный вызов onSelectedChanged(RecyclerView.ViewHolder, int)
предоставляет информацию о текущем actionState:
- ACTION_STATE_IDLE
:
- ACTION_STATE_DRAG
- ACTION_STATE_SWIPE
Итак, вы можете отслеживать, изменился ли порядок, и когда состояние изменится на ACTION_STATE_IDLE
, вы можете делать то, что вам нужно!
Пример:
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;
}
}
Ответ 3
Вы должны внедрить слушатель OnMove в свой адаптер:
Collections.swap(youCoolList, fromPosition, toPosition); notifyItemMoved(fromPosition, toPosition);
как этот человек делает https://medium.com/@ipaulpro/drag-and-swipe-with-recyclerview-b9456d2b1aaf#.blviq6jxp
специальный пример сетки https://medium.com/@ipaulpro/drag-and-swipe-with-recyclerview-6a6f0c422efd#.xb74uu7ke
Ответ 4
Я провел несколько тестов, и onSelectedChanged(RecyclerView.ViewHolder?, Int)
показался мне наиболее надежным для обнаружения конца жеста (отбрасывания). Метод вызывается всякий раз, когда элемент перетаскивается и передается состояние действия ACTION_STATE_DRAG
. Когда перетаскивание закончено, оно вызывается с состоянием действия ACTION_STATE_IDLE
.
Смотрите мое решение ниже. onItemDrag(Int, Int)
используется для изменения порядка элементов в адаптере при перетаскивании элемента. С другой стороны onItemDragged(Int, Int)
обратный вызов onItemDragged(Int, Int)
предназначен для обновления позиций в базе данных в конце жеста.
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
}
}
}
}
}