自定义监听事件——recyclerview上下拖拽左右滑动删除

时间:2024-03-21 10:00:28

昨天在改公司项目bug的时候遇到一个问题,recyclerview列表中存在拖拽、左右滑动删除,但仅仅停留在UI效果上,滑动删除之后并未达到实际删除数据的功能,而且删除后会在原来的位置留下空白。一路跟踪,发现原开发人员写了一个帮助类RecyclerViewItemTouchHelper,继承于ItemTouchHelper.Callback,声明了一个mItemMoveListener,并重写若干方法:

public class RecyclerViewItemTouchHelper extends ItemTouchHelper.Callback {

    ItemMoveListener mItemMoveListener;
    
    public RecyclerViewItemTouchHelper(ItemMoveListener mItemMoveListener) {
        this.mItemMoveListener = mItemMoveListener;
    }

    /**
     * 获取动作标识
     * 动作标识分:dragFlags和swipeFlags
     * dragFlags:列表滚动方向的动作标识(如竖直列表就是上和下,水平列表就是左和右)
     * wipeFlags:与列表滚动方向垂直的动作标识(如竖直列表就是左和右,水平列表就是上和下)
     */
    @Override
    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
        int swipeFlags = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
        int flags = makeMovementFlags(dragFlags, swipeFlags);
        return flags;
    }

    /**
     * 是否开启item长按拖拽功能
     */
    @Override
    public boolean isLongPressDragEnabled() {
        return true;
    }

    /**
     * 当item拖拽移动时触发
     *
     * @param viewHolder       当前被拖拽的item的viewHolder
     * @param targetViewHolder 当前被拖拽的item下方的另一个item的viewHolder
     */
    @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder,
                          RecyclerView.ViewHolder targetViewHolder) {
        return mItemMoveListener.onItemMove(viewHolder, targetViewHolder, viewHolder.getAdapterPosition(),
                targetViewHolder.getAdapterPosition());
    }

    /**
     * 当item被拖拽或侧滑时触发
     *
     * @param actionState 当前item的状态
     */
    @Override
    public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
        super.onSelectedChanged(viewHolder, actionState);
        //不管是拖拽或是侧滑,背景色都要变化
        if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
            viewHolder.itemView.setBackgroundColor(
                    viewHolder.itemView.getContext().getResources().getColor(R.color.cdcdcdc));
        }
    }

    /**
     * 当item的交互动画结束时触发
     */
    @Override
    public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        super.clearView(recyclerView, viewHolder);
        viewHolder.itemView.setBackgroundColor(
                viewHolder.itemView.getContext().getResources().getColor(android.R.color.white));

        viewHolder.itemView.setAlpha(1);
        viewHolder.itemView.setScaleY(1);
    }

    /**
     * 当item侧滑出去时触发(竖直列表是侧滑,水平列表是竖滑)
     */
    @Override
    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
    }

    @Override
    public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY,
                            int actionState, boolean isCurrentlyActive) {
        super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
        if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
            float value = 1 - Math.abs(dX) / viewHolder.itemView.getWidth();
            viewHolder.itemView.setAlpha(value);
            viewHolder.itemView.setScaleY(value);
        }
    }

    public interface ItemMoveListener {

        boolean onItemMove(RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder targetViewHolder,
                           int fromPosition, int toPosition);
    }
}
 

在代码最后定义了一个接口ItemMoveListener,写了一个抽象方法onItemMove(),用于对recycler view中子项布局发生移动的状态进行监听。

 在RecyclerViewItemTouchHelper重写的onMove()方法中返回接口方法。

自定义监听事件——recyclerview上下拖拽左右滑动删除

在fragment实现此接口,重写onItemMove()方法(回调?)

自定义监听事件——recyclerview上下拖拽左右滑动删除 可以看到在这个方法中实现了对拖拽子项的监听,当检测到拖拽动作时,做了交换数据并更换item位置等操作。

然后实例化了一个viewItemTouchHelper对象,创建一个itemTouchHelper对象,最后和recyclerview做关联。

自定义监听事件——recyclerview上下拖拽左右滑动删除 这样就实现了对拖拽动作的监听。

这样分析了一遍流程,那么我想添加一个对滑动删除的监听应该怎么做呢?我们回到最开始的RecyclerViewItemTouchHelper类,发现重写了个onSwiped()方法,注释清楚的写了“当item侧滑出去时触发”,那这个问题就简单了。

依葫芦画瓢:

public class RecyclerViewItemTouchHelper extends ItemTouchHelper.Callback {

    ItemMoveListener mItemMoveListener;
    ItemSwipeListener mItemSwipeListener;

    public RecyclerViewItemTouchHelper(ItemMoveListener mItemMoveListener, ItemSwipeListener mItemSwipeListener) {
        this.mItemMoveListener = mItemMoveListener;
        this.mItemSwipeListener = mItemSwipeListener;
    }

    /**
     * 获取动作标识
     * 动作标识分:dragFlags和swipeFlags
     * dragFlags:列表滚动方向的动作标识(如竖直列表就是上和下,水平列表就是左和右)
     * wipeFlags:与列表滚动方向垂直的动作标识(如竖直列表就是左和右,水平列表就是上和下)
     */
    @Override
    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
        int swipeFlags = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
        int flags = makeMovementFlags(dragFlags, swipeFlags);
        return flags;
    }

    /**
     * 是否开启item长按拖拽功能
     */
    @Override
    public boolean isLongPressDragEnabled() {
        return true;
    }

    /**
     * 当item拖拽移动时触发
     *
     * @param viewHolder       当前被拖拽的item的viewHolder
     * @param targetViewHolder 当前被拖拽的item下方的另一个item的viewHolder
     */
    @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder,
                          RecyclerView.ViewHolder targetViewHolder) {
        return mItemMoveListener.onItemMove(viewHolder, targetViewHolder, viewHolder.getAdapterPosition(),
                targetViewHolder.getAdapterPosition());
    }

    /**
     * 当item被拖拽或侧滑时触发
     *
     * @param actionState 当前item的状态
     */
    @Override
    public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
        super.onSelectedChanged(viewHolder, actionState);
        //不管是拖拽或是侧滑,背景色都要变化
        if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
            viewHolder.itemView.setBackgroundColor(
                    viewHolder.itemView.getContext().getResources().getColor(R.color.cdcdcdc));
        }
    }

    /**
     * 当item的交互动画结束时触发
     */
    @Override
    public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        super.clearView(recyclerView, viewHolder);
        viewHolder.itemView.setBackgroundColor(
                viewHolder.itemView.getContext().getResources().getColor(android.R.color.white));

        viewHolder.itemView.setAlpha(1);
        viewHolder.itemView.setScaleY(1);
    }

    /**
     * 当item侧滑出去时触发(竖直列表是侧滑,水平列表是竖滑)
     */
    @Override
    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
        mItemSwipeListener.onItemSwiped(viewHolder, direction);
    }

    @Override
    public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY,
                            int actionState, boolean isCurrentlyActive) {
        super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
        if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
            float value = 1 - Math.abs(dX) / viewHolder.itemView.getWidth();
            viewHolder.itemView.setAlpha(value);
            viewHolder.itemView.setScaleY(value);
        }
    }

    public interface ItemMoveListener {

        boolean onItemMove(RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder targetViewHolder,
                           int fromPosition, int toPosition);
    }

    public interface ItemSwipeListener {

        void onItemSwiped(RecyclerView.ViewHolder viewHolder, int direction);
    }
}

自定义监听事件——recyclerview上下拖拽左右滑动删除 自定义监听事件——recyclerview上下拖拽左右滑动删除

自定义监听事件——recyclerview上下拖拽左右滑动删除

1.定义一个接口ItemSwipeListener,在接口中定义方法onItemSwiped();

2.在RecyclerViewItemTouchHelper类中声明接口,属性写入构造函数,并在onSwiped()中调用接口方法;

3.在fragment中实现接口,并重写接口方法,通过构造函数实例化RecyclerViewItemTouchHelper。

其实这里第二点,也可以通过写个setListener方法将实例传进来,adapter.setXXXListener()然后new 这个接口通过匿名内部类的方式,直接自动重写方法,都是一样的。

在这里可以总结下,在使用recyclerview时什么情况下使用自定义监听,就我遇到的情况来说,大都是需要在外部对子项进行监听时,包括上面提到的拖拽、滑动,以及长按监听等,就是仅仅通过adpter不能达到我们想要的效果。我能想到的就是这样,如果不对,请大神们多多指正。