RecyclerView根据Item类型开关长按顺序拖拽

2,670

效果演示


需求介绍

根据数据类型动态的改变每个item可操作状态,实现某些类型的item不响应长按拖拽

相信大家对于RecyclerView的长按拖拽并不陌生,通常我们会使用官方自带的ItemTouchHelper实现 通常来说只需要重写ItemTouchHelper.Callback的对应方法就可以实现一个简单的拖拽,但是对于某些类型Item不允许拖拽滑动的情况实现起来可能会比较麻烦。

具体实现

public class DefaultItemTouchHelperCallback extends ItemTouchHelper.Callback {

    private OnItemTouchCallbackListener l;
    //当前item可拖拽状态
    private boolean currentPositionLongPressEnabled = true;

    //整个列表的可操作状态 (总开关)
    private boolean isLongPressEnabled;
    private boolean isItemSwipeEnabled;


    public DefaultItemTouchHelperCallback(OnItemTouchCallbackListener onItemTouchCallbackListener) {
        if (onItemTouchCallbackListener == null) {
            throw new NullPointerException("OnItemTouchCallbackListener is null");
        }
        this.l = onItemTouchCallbackListener;
        isLongPressEnabled = l.isLongPressDragEnabled();
        isItemSwipeEnabled = l.isItemViewSwipeEnabled();
    }

    @Override
    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {

        int position = viewHolder.getAdapterPosition();
        if (isLongPressEnabled)
            currentPositionLongPressEnabled = l.currentPositionLongPressEnabled(position);

        RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
        if (layoutManager instanceof GridLayoutManager) {// GridLayoutManager
            // flag如果值是0,相当于这个功能被关闭
            int dragFlag = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT | ItemTouchHelper.UP | ItemTouchHelper.DOWN;
            int swipeFlag = 0;
            // create make
            return makeMovementFlags(dragFlag, swipeFlag);
        } else if (layoutManager instanceof LinearLayoutManager) {// linearLayoutManager
            LinearLayoutManager linearLayoutManager = (LinearLayoutManager) layoutManager;
            int orientation = linearLayoutManager.getOrientation();

            int dragFlag = 0;
            int swipeFlag = 0;
            // 为了方便理解,相当于分为横着的ListView和竖着的ListView
            if (orientation == LinearLayoutManager.HORIZONTAL) {// 如果是横向的布局
                swipeFlag = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
                dragFlag = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
            } else if (orientation == LinearLayoutManager.VERTICAL) {// 如果是竖向的布局,相当于ListView
                dragFlag = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
                swipeFlag = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
            }
            return makeMovementFlags(dragFlag, swipeFlag);
        }
        return 0;
    }

    @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
        int fromPosition = viewHolder.getAdapterPosition();
        int targetPosition = target.getAdapterPosition();
        //如果fromPosition or targetPosition不可操作则直接返回false 不进行数据交换回调
        return l.currentPositionLongPressEnabled(fromPosition)
                && l.currentPositionLongPressEnabled(targetPosition)
                && l.onMove(fromPosition, targetPosition);
    }

    @Override
    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
        l.onSwiped(viewHolder.getAdapterPosition());
    }

    @Override
    public void onMoved(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, int fromPos, RecyclerView.ViewHolder target, int toPos, int x, int y) {
        super.onMoved(recyclerView, viewHolder, fromPos, target, toPos, x, y);
        l.onMoved(recyclerView, viewHolder, fromPos, target, toPos, x, y);
    }

    @Override
    public boolean isLongPressDragEnabled() {
        return isLongPressEnabled && currentPositionLongPressEnabled;
    }

    @Override
    public boolean isItemViewSwipeEnabled() {
        return isItemSwipeEnabled;
    }

    public interface OnItemTouchCallbackListener {
        /**
         * @param position 当前位置是否可长按拖拽
         * @return 默认true 可拖拽
         */
        default boolean currentPositionLongPressEnabled(int position) {
            return true;
        }

        /**
         * 所有item是否可拖拽
         *
         * @return 默认true 可拖拽
         */
        default boolean isLongPressDragEnabled() {
            return true;
        }

        /**
         * 所有item是否可滑动删除
         *
         * @return 默认true 可删除
         */
        default boolean isItemViewSwipeEnabled() {
            return true;
        }

        /**
         * @param position 滑动删除后位置
         */
        default void onSwiped(int position) {
        }

        /**
         * @param fromPosition   开始位置
         * @param targetPosition 结束位置
         * @return 是否处理
         */
        boolean onMove(int fromPosition, int targetPosition);


        default void onMoved(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, int fromPos, RecyclerView.ViewHolder target, int toPos, int x, int y) {

        }
    }
}

getMovementFlags():这个方法大家应该都比较了解了 在用户交互的时候返回当前item可滑动拖拽的方向参数 内部进行了简单的实现,根据不同的Manager LinearLayoutManager或GridLayoutManager进行了默认的方向设置

        int position = viewHolder.getAdapterPosition();
        if (isLongPressEnabled)
            currentPositionLongPressEnabled = l.currentPositionLongPressEnabled(position);

这部分代码就是主要的控制当前Item可操作的关键部分了,我们把对应位置传给接口实例具体这个item是否可拖拽只需要实现对应方法即可

onMove():这个方法通常在移动的时候替换对应的数据并刷新View

        int fromPosition = viewHolder.getAdapterPosition();
        int targetPosition = target.getAdapterPosition();
        //如果fromPosition or targetPosition不可操作则直接返回false 不进行数据交换回调
        return l.currentPositionLongPressEnabled(fromPosition)
                && l.currentPositionLongPressEnabled(targetPosition)
                && l.onMove(fromPosition, targetPosition);

获取开始和目标的位置,根据接口实现判断是否可操作,如果2个position中有1个不可操作(不可移动) 那么此次拖拽就不应该交换位置,既没有回调onMove

    @Override
    public boolean isLongPressDragEnabled() {
        return isLongPressEnabled && currentPositionLongPressEnabled;
    }

    @Override
    public boolean isItemViewSwipeEnabled() {
        return isItemSwipeEnabled ;
    }

因为ItemTouchHelper内部事件监听每次都会调用对应的Enabled方法 此处使用总开关和当前item的开关进行控制

OnItemTouchCallbackListener: 接口部分提供了对应的总开关和item开关,开关都默认返回了true开启状态。对于不需要的功能, 重写返回false即可。

效果演示的callback代码

    private void initItemTouchHelper() {
        DefaultItemTouchHelperCallback callback = new DefaultItemTouchHelperCallback(new DefaultItemTouchHelperCallback.OnItemTouchCallbackListener() {
            @Override
            public boolean onMove(int fromPosition, int targetPosition) {

                List<PictureDraftBean.PicturesBean> imagePaths = adapter.datas;
                //替换数据
                Collections.swap(imagePaths, fromPosition, targetPosition);
                //刷新
                adapter.notifyItemMoved(fromPosition, targetPosition);

                return true;
            }

            @Override
            public boolean currentPositionLongPressEnabled(int position) {
                //最后一个item是添加图片
                return position != adapter.datas.size();
            }

第一次写文章,如果错误和疑问请指出。:)