ItemTouchHelper of 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、RecyclerViewでまだアイテムをドラッグしているときにItemTouchHelperのonMoved関数が機能するのはなぜですか?これを実現するにはどうすればよいですか?
アイテムのドラッグアンドドロップ中に、onMove()を複数回呼び出すことができますが、clearView()は1回呼び出されます。したがって、これを使用して、ドラッグが終了した(ドロップが発生した)ことを示すことができます。そして、2つの変数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);
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;
}
}
アダプタに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
いくつかのテストを行いましたが、onSelectedChanged(RecyclerView.ViewHolder?, Int)
はジェスチャー(ドロップ)の終了を検出するのに最も信頼できるように思えました。このメソッドは、アイテムがドラッグされ、_ACTION_STATE_DRAG
_のアクション状態が渡されるたびに呼び出されます。ドラッグが終了すると、_ACTION_STATE_IDLE
_のアクション状態で呼び出されます。
以下のソリューションをご覧ください。 onItemDrag(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
}
}
}
}
}
_