两个列表间实现拖拽编辑

需要实现的效果如下:
1.列表自身可以拖拽交换
2.下边列表可以拖动到上边指定位置添加
3.下边列表拖入上方空白区域添加到末尾

image

一、同一个列表拖动编辑

方案比较简单,直接使用recyclerView 自身提供的API 就能实现

// 1 定义变量
private ItemTouchHelper itemTouchHelper;
private QuickSettingItemTouchHelperCallback svItemTouchHelperCallback;



private QuickSettingItemTouchHelperCallback getItemTouchCallBack() {
        if (svItemTouchHelperCallback == null) {
            svItemTouchHelperCallback = new QuickSettingItemTouchHelperCallback(
                    (ArrayList<ISVShortcut>) getPresenter().getSavedShortCutList(ShortcutManager.TYPE_ADDED_SETTING),
                    addedAdapter);
        }
        return svItemTouchHelperCallback;
    }

// 2 调用
itemTouchHelper = new ItemTouchHelper(getItemTouchCallBack());
itemTouchHelper.attachToRecyclerView(recyclerView);

// 3 处理拖拽逻辑
public class QuickSettingItemTouchHelperCallback extends ItemTouchHelper.Callback {
    private static final String TAG = "MyItemTouchHelper";
    private ArrayList<ISVShortcut> mList;
    private final ShortcutAdapter recycleViewAdapter;
    // 是否开启拖拽的标记
    private boolean dragEnable = false;

    public QuickSettingItemTouchHelperCallback(ArrayList<ISVShortcut> list, ShortcutAdapter recycleViewAdapter) {
        this.mList = list;
        this.recycleViewAdapter = recycleViewAdapter;
        recycleViewAdapter.setUpdateItemHelperListener(list1 -> mList = list1);
    }

    @Override
    public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
        final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.START | ItemTouchHelper.END;
        final int swipeFlags = 0;
        return makeMovementFlags(dragFlags, swipeFlags);
    }

    /**
     * 拖动中
     */
    @Override
    public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
        recyclerView.getParent().requestDisallowInterceptTouchEvent(true);
        //得到当前拖拽的viewHolder的Position
        int fromPosition = viewHolder.getBindingAdapterPosition();
        //拿到当前拖拽到的item的viewHolder
        int toPosition = target.getBindingAdapterPosition();
        if (fromPosition < toPosition) {
            for (int i = fromPosition; i < toPosition; i++) {
                Collections.swap(mList, i, i + 1);
            }
        } else {
            for (int i = fromPosition; i > toPosition; i--) {
                Collections.swap(mList, i, i - 1);
            }
        }
        recycleViewAdapter.notifyItemMoved(fromPosition, toPosition);
        recycleViewAdapter.updateDataList(mList, false);
        Log.d(TAG, " fromPosition : " + fromPosition + " toPosition : " + toPosition);
        return true;
    }

    @Override
    public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {

    }

    /**
     * 长按选中Item回调
     */
    @Override
    public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
        super.onSelectedChanged(viewHolder, actionState);
    }

    /**
     * 手指松开的时候回调
     */
    @Override
    public void clearView(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
        super.clearView(recyclerView, viewHolder);
    }

    public void setDragEnable(boolean dragEnable) {
        this.dragEnable = dragEnable;
    }

    /**
     * 重写拖拽不可用
     */
    @Override
    public boolean isLongPressDragEnabled() {
        return dragEnable;
    }
}

二、列表间的拖动

采用的是 View.startDragAndDrop 和 setOnDragListener 方法来实现相关功能(支持跨view)
参考文档

1、发起拖拽

// 给item设置长按换发起拖拽 
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
	// 设置一下代码,拖拽就能生效了
	holder.itemView.setOnLongClickListener(view -> {
            // 编辑模式,开启拖拽
            if (dragEnable) {
                // 拖拽后
                Log.d(TAG, "post DragOpt.FROM_DEL");
                ShortCutEvent fromEvent = new ShortCutEvent(this, statusShortcut, position);
                fromEvent.setAdd(statusShortcut.isAdded());
                QuickSettingDragHelper.startDrag(fromEvent);
				// 传参,同一个window或者activity 里面,用传参的方式就能实现数据交互
                ClipData data = ClipData.newPlainText("", String.valueOf(position));
				// 拖拽背景(拖拽实跟随手势移动显示的view)
                View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(holder.shortCutView.getViewBg());
                view.startDragAndDrop(data, shadowBuilder, view, 0);
                view.setVisibility(View.INVISIBLE);
            }
            return true;
	});

}

2.监听拖拽事件

// 拖拽到item(带pos) 生效
holder.itemView.setOnDragListener(getDragInstance());
// 拖拽到 recyclerView 空白处(不带pos,添加至末尾)
recyclerView.setOnDragListener(getDragInstance());

主要实现逻辑在 DragListener 中实现,定义如下

public static class DragListener implements View.OnDragListener {

        boolean isDropped = false;
        boolean cancelDrag = false;
        ShortcutAdapter.ShortcutListener shortcutListener;

        public DragListener(ShortcutAdapter.ShortcutListener IFromDataListener) {
            this.shortcutListener = IFromDataListener;
        }

        @Override
        public boolean onDrag(View v, DragEvent event) {
            int action = event.getAction();
            RecyclerView toView = null;
            int positionTo = -1;
            switch (action) {
                case DragEvent.ACTION_DRAG_STARTED:
                    cancelDrag = false;
                    Log.d(TAG, "ACTION_DRAG_STARTED: ");
                    break;
                case DragEvent.ACTION_DRAG_LOCATION:
                    Log.d(TAG, "ACTION_DRAG_LOCATION: ");
                    break;
                case DragEvent.ACTION_DROP:
                    Log.d(TAG, "ACTION_DROP: -------------");
                    // item 拖拽view 变化
                    v.setAlpha(1);
                    isDropped = true;
					// 判断是 目的 列表的item 还是空白处
                    if (v.getId() == R.id.recyclerview) {
                        Log.d(TAG, " drop to  rv");
                    } else {
                        Log.d(TAG, "ACTION_DROP  to item");
                    }
                   ...完成拖拽 数据更新,列表更新
                    return true;
                case DragEvent.ACTION_DRAG_ENDED:
                    Log.d(TAG, "ACTION_DRAG_ENDED: cancelDrag " + cancelDrag + " \t isDropped :" + isDropped);
                    if (!cancelDrag && !isDropped) {
                        QuickSettingDragHelper.cancelDrag();
                        return true;
                    }
                    QuickSettingDragHelper.clearEvent();
                    break;
                case DragEvent.ACTION_DRAG_ENTERED:
				// 进入目标view
                    Log.d(TAG, "ACTION_DRAG_ENTERED: ");
                    if (v.getId() == R.id.recyclerview) {
                        toView = (MaxHeightRecyclerView) v;
						// 获取pos 
                        positionTo = toView.getAdapter().getItemCount();
                    } else {
                        toView = (MaxHeightRecyclerView) v.getParent();
                        positionTo = (int) v.getTag();
                        v.setAlpha(0.5f);
                    }
                    ShortcutAdapter adapterTo = (ShortcutAdapter) toView.getAdapter();
                    if (QuickSettingDragHelper.getFromEvent().getAdapter() == adapterTo) {
                        Log.d(TAG, "onDrag:同一个列表");
                        break;
                    }
                    Log.d(TAG, "positionTo " + positionTo);
                    ShortCutEvent toEvent1 = new ShortCutEvent(adapterTo, new PlaceholderShortCut(null), positionTo);
                    QuickSettingDragHelper.setToEvent(toEvent1);
                    break;
                case DragEvent.ACTION_DRAG_EXITED:
                    Log.d(TAG, "ACTION_DRAG_EXITED: " + v);
                    if (v.getAlpha() < 1) {
                        v.setAlpha(1);
                    }
                    cancelDrag = true;
                    break;
                default:
                    break;
            }

            if (!isDropped) {
                View vw = (View) event.getLocalState();
                vw.setVisibility(View.VISIBLE);
            }

            return true;
        }

    }

以上就是拖拽的过程中各个阶段回调,类似一次触摸事件的 down、up、cancel 等;

posted @ 2023-06-05 16:35  阿丟啊  阅读(439)  评论(2编辑  收藏  举报