【问题标题】:android - how to catch Drop action of ItemTouchHelper which is used with RecyclerViewandroid - 如何捕捉与 RecyclerView 一起使用的 ItemTouchHelper 的 Drop 动作
【发布时间】:2016-06-25 12:56:26
【问题描述】:

RecyclerViewItemTouchHelper有问题。

我正在制作游戏。游戏板实际上是一个 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);
}

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

【问题讨论】:

    标签: android drag-and-drop android-recyclerview gridlayoutmanager


    【解决方案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) 如下:

    public void onItemMove(int fromPosition, int toPosition) {
        list.add(toPosition, list.remove(fromPosition));
        notifyItemMoved(fromPosition, toPosition);
    }
    

    【讨论】:

    • 我在看这个:=)
    • 按预期工作
    • 我一直在试图弄清楚如何知道下降已经完成。您对 clearView() 的解释是完美的。也感谢您展示实现。
    • 这是要走的路!
    • 这不是正确的做法,因为当一个视图被 recyclerview 以某种方式废弃时,可以调用clearView!这会导致调用子序列逻辑的时间不正确,这可能会导致崩溃。使用onSelectedChanged 是正确的方法。在onSelectedChanged 中,您可以检查viewHolder == null 和IDLE 状态,这意味着丢弃已完成(因此选择设置为null
    【解决方案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;
        }
    }
    

    【讨论】:

    • 这也是我的直观答案,但显然它没有按预期工作......
    • 我成功地使用了这样的方法,每次调用onSelectedChanged 时都会跟踪之前的状态——如果它曾经是ACTION_STATE_DRAG,现在是ACTION_STATE_IDLE,那么doSomething()
    • 非常适合我!
    【解决方案3】:

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

    【讨论】:

    • 对我来说,viewHolder 始终为空!
    【解决方案4】:

    您必须在适配器中实现 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

    【讨论】:

    • 当视图被拖动而不是被放下时调用。这是行不通的
    猜你喜欢
    • 1970-01-01
    • 2017-02-05
    • 2022-01-25
    • 1970-01-01
    • 2019-09-27
    • 2020-10-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多