android - 如何捕获与RecyclerView一起使用的ItemTouchHelper的Drop动作

oka*_*ose 30 android drag-and-drop gridlayoutmanager android-recyclerview

我对RecyclerView的ItemTouchHelper疑问.

我正在制作一个游戏.游戏板实际上是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);
}
Run Code Online (Sandbox Code Playgroud)

那么,当我仍然在RecyclerView上拖动项目时,为什么ItemTouchHelper的onMoved函数有效?我怎样才能做到这一点?

小智 72

在拖放项目时,可以多次调用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;
    }

};
Run Code Online (Sandbox Code Playgroud)

adapter.onItemMove(fromPosition,toPosition)如下所示:

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

  • 这不是正确的方法,因为当 viewholder 被 recyclerview 以某种方式废弃时,可以调用“clearView”!这会导致调用子序列逻辑的时间不正确,从而可能导致崩溃。使用“onSelectedChanged”是正确的方法。在`onSelectedChanged`中,您可以检查`viewHolder == null`和IDLE状态,这意味着放置已完成(因此selected设置为`null`) (3认同)
  • 我在看这个:=) (2认同)

Tim*_*otz 7

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;
    }
}
Run Code Online (Sandbox Code Playgroud)


pgi*_*cek 7

我做了一些测试,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
                }
            }
        }
    }

}
Run Code Online (Sandbox Code Playgroud)


Lam*_*ine 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