使用 GridLayoutManager 在 RecyclerView 中拖放项目 [英] Drag and drop items in RecyclerView with GridLayoutManager

查看:21
本文介绍了使用 GridLayoutManager 在 RecyclerView 中拖放项目的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想要达到的目标:有一个带有 GridLayoutManager 的 RecyclerView,它支持拖放并在拖动时重新排列项目.

What I want to achieve: Have a RecyclerView with GridLayoutManager that supports drag'n'drop and that rearranges the items while dragging.

旁注:第一次使用拖放开发任何东西.

Side note: First time developing anything with drag and drop.

有很多关于如何使用 ListView 实现此功能的主题,例如:https://raw.githubusercontent.com/btownrippleman/FurthestProgress/master/FurthestProgress/src/com/anappforthat/android/languagelineup/DynamicListView.java

There are a lot of topics on how to achieve this feature using a ListView, for example: https://raw.githubusercontent.com/btownrippleman/FurthestProgress/master/FurthestProgress/src/com/anappforthat/android/languagelineup/DynamicListView.java

然而,这些示例通常有很多代码,用于创建拖动视图的位图,感觉应该可以使用 View.startDrag(...) 和RecyclerView 带有 notifyItemAdded()notifyItemMoved()notifyItemRemoved(),因为它们提供了重新排列的动画.

However the examples are usually a lot of code with, creating bitmaps of the dragged view and it feels like it should be possible to achieve the same result using View.startDrag(...) and RecyclerView with notifyItemAdded(), notifyItemMoved() and notifyItemRemoved() since they provide rearrange animations.

所以我玩了一些并想出了这个:

So I played around some and came up with this:

final CardAdapter adapter = new CardAdapter(list);
adapter.setHasStableIds(true);
adapter.setListener(new CardAdapter.OnLongClickListener() {
    @Override
    public void onLongClick(View view) {
        ClipData data = ClipData.newPlainText("","");
        View.DragShadowBuilder builder = new View.DragShadowBuilder(view);
        final int pos = mRecyclerView.getChildAdapterPosition(view);
        final Goal item = list.remove(pos);

        mRecyclerView.setOnDragListener(new View.OnDragListener() {
            int prevPos = pos;

            @Override
            public boolean onDrag(View view, DragEvent dragEvent) {
                final int action = dragEvent.getAction();
                switch(action) {
                    case DragEvent.ACTION_DRAG_LOCATION:
                        View onTopOf = mRecyclerView.findChildViewUnder(dragEvent.getX(), dragEvent.getY());
                        int i = mRecyclerView.getChildAdapterPosition(onTopOf);

                        list.add(i, list.remove(prevPos));
                        adapter.notifyItemMoved(prevPos, i);
                        prevPos = i;
                        break;

                    case DragEvent.ACTION_DROP:
                        View underView = mRecyclerView.findChildViewUnder(dragEvent.getX(), dragEvent.getY());
                        int underPos = mRecyclerView.getChildAdapterPosition(underView);

                        list.add(underPos, item);
                        adapter.notifyItemInserted(underPos);
                        adapter.notifyDataSetChanged();
                        break;
                }

                return true;
            }
        });

        view.startDrag(data, builder, view, 0);
    }
});
mRecyclerView.setAdapter(adapter);

这段代码工作正常,我得到了交换,但非常不稳定/不稳定,有时当它刷新时,整个网格会重新排列回原始顺序或随机排列.无论如何,上面的代码只是我的第一次快速尝试,我真正想知道的是是否有一些标准/最佳实践方法可以使用 ReyclerView 进行拖放操作,或者解决它的正确方法是否仍然相同多年来一直用于 ListViews?

This piece of code sort of work, I get the swapping, but very unstable/shaky and sometimes when it's refreshing the whole grid is rearranged back to original order or to something random. Anyway the code above is just my first quick attempt, what I'm really more interested in knowing is if there's some standard/best practice way of doing the drag and drop with ReyclerView's or if the correct way of solving it is still the same that's been used for ListViews for years?

推荐答案

实际上有更好的方法来实现这一点.您可以使用一些 RecyclerView 的同伴"类:

There is actually a better way to achieve this. You can use some of the RecyclerView's "companion" classes:

ItemTouchHelper,即

一个实用程序类,用于添加滑动以关闭和拖动 &放弃对 RecyclerView 的支持.

a utility class to add swipe to dismiss and drag & drop support to RecyclerView.

及其ItemTouchHelper.Callback,即

ItemTouchHelper 与您的应用程序之间的契约

the contract between ItemTouchHelper and your application

// Create an `ItemTouchHelper` and attach it to the `RecyclerView`
ItemTouchHelper ith = new ItemTouchHelper(_ithCallback);
ith.attachToRecyclerView(rv);

// Extend the Callback class
ItemTouchHelper.Callback _ithCallback = new ItemTouchHelper.Callback() {
    //and in your imlpementaion of
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
        // get the viewHolder's and target's positions in your adapter data, swap them
        Collections.swap(/*RecyclerView.Adapter's data collection*/, viewHolder.getAdapterPosition(), target.getAdapterPosition());
        // and notify the adapter that its dataset has changed
        _adapter.notifyItemMoved(viewHolder.getAdapterPosition(), target.getAdapterPosition());
        return true;
    }

    @Override
    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
        //TODO    
    }

    //defines the enabled move directions in each state (idle, swiping, dragging). 
    @Override
    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        return makeFlag(ItemTouchHelper.ACTION_STATE_DRAG,
                ItemTouchHelper.DOWN | ItemTouchHelper.UP | ItemTouchHelper.START | ItemTouchHelper.END);
    }
};

有关更多详细信息,请查看他们的文档.

For more details check their documentation.

这篇关于使用 GridLayoutManager 在 RecyclerView 中拖放项目的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆