RecyclerView(六):ItemTouchHelper如何实现拖拽和侧滑删除

概述

对于RecyclerView子项的滑动或是对item进行装饰前面都已经提到过了,现在就来说说对item的拖拽进行排序和侧滑删除该如何实现,对于这样的功能,RecyclerView是没有直接给我们提供api的,不过提供了工具类方便我们使用,这个类就是这里要说的ItemTouchHelper,这个类已经为我们处理好了相对应的动画效果,但是对应的数据处理就需要我们自己来做,所以,对于这个类的理解和使用,主要还是理解他提供的几个抽象方法,同时他继承了RecyclerView.ItemDecoration这个类,前面说过它主要是对item进行装饰,对item拖拽的动画效果就是通过他来实现的,同时ItemTouchHelper还对手势进行了拦截,这样就可以对对应手势事件进行处理,比如拖动侧滑。首先是对RecyclerView进行绑定:

绑定

调用ItemTouchHelper的attachToRecyclerView()方法进行绑定。创建ItemTouchHelper时需要传ItemTouchHelper.Callback,这个回调就是我们需要实现的重点,想实现什么样的功能实现对应的方法就可以看了。

拖拽和滑动方向的设定

对于是否允许拖拽和拖动是可以进行设置的,ItemTouchHelper.Callback默认是允许拖拽和侧滑的,如果你不想拖拽或是滑动,那么只需要重写对应的方法就可以了:
是否允许拖拽,根据返回值来确定是否允许拖拽,true是允许拖拽,false是不允许拖拽:

@Override
    public boolean isLongPressDragEnabled() {
        return true;
    }

是否允许侧滑,根据返回值来确定是否允许侧滑,true是允许侧滑,false是不允许侧滑:

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

在允许侧滑和拖拽的情况下,接下来就可以对方向进行设定了,可以实现如下方法进行设定:

    @Override
    public int getMovementFlags(RecyclerView recyclerView, ViewHolder
            viewHolder) {
        // 拖拽的标记,这里允许上下左右四个方向
        int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
        // 滑动的标记,这里允许左右滑动
        int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;
        Log.d(TAG,"getMovementFlags...........");
        return makeMovementFlags(dragFlags, swipeFlags);
    }
    
	public static int makeMovementFlags(int dragFlags, int swipeFlags) {
            return makeFlag(ACTION_STATE_IDLE, swipeFlags | dragFlags)
                    | makeFlag(ACTION_STATE_SWIPE, swipeFlags)
                    | makeFlag(ACTION_STATE_DRAG, dragFlags);
        }

可以看到对拖拽和滑动是使用不同的标记的,如果你只是想设置某一个,可以直接使用makeFlag()进行设定,这样做完后,就可以对item进行对应的拖拽和侧滑了,但是,ItemTouchHelper.Callback并没有对数据进行处理,只是在对应的状态会有对应的回调,实际的数据处理还是需要我们自己来的,这里先来说下对拖拽得数据处理,在拖拽到对应位置,会调用到onMove()方法,该方法是一个抽象方法,所以我们必须去实现:

    @Override
    public boolean onMove(RecyclerView recyclerView, ViewHolder viewHolder,
                          ViewHolder target) {
        return true;
    }

返回是一个Boolean值,通常返回的是true,这是为了拖动的view是可见的,这个方法会在拖动的item占用另一个item时调用,也就是拖动item的位置被其他item占用时会被调用到。在这里就可以进行对数据的处理,拖拽的时候,改变的只是item的界面显示,在这里我们就可以对数据进行变换:

    @Override
    public boolean onMove(RecyclerView recyclerView, ViewHolder viewHolder,
                          ViewHolder target) {
        // 移动时更改列表中对应的位置并返回true
        int adapterPosition = viewHolder.getAdapterPosition();
        int targetPosition = target.getAdapterPosition();
        Log.d(TAG, "onMoved: "+ adapterPosition +"   "+ targetPosition);
        Collections.swap(data, adapterPosition, targetPosition);
        // 移动完成后刷新列表
        adapter.notifyItemMoved(adapterPosition, targetPosition);
        return true;
    }

这样完成后,对拖拽的逻辑处理就算完了,接下来就是侧滑删除了,在侧滑成功时,会调用到onSwiped()方法,对数据的处理当然也是在这里了:

    @Override
    public void onSwiped(final ViewHolder viewHolder, int direction) {
        Log.d(TAG, "onSwiped: "+viewHolder.getAdapterPosition()+" "+data.size());
        data.remove(viewHolder.getAdapterPosition());
        // 刷新列表
        adapter.notifyItemRemoved(viewHolder.getAdapterPosition());
    }

将对应位置的数据移除,然后刷新列表就可以了,也是很简单,当然,ItemTouchHelper.Callback提供的方法不止这些,如果你还需要做一些其他对应的处理,那么可以去找对应的api就可以了,比如item选中和释放时,就会调onSelectedChanged()方法,下面贴一份简单的实现代码:

public class MyItemTouchHelper extends ItemTouchHelper.Callback {

    private static final String TAG = "MyItemTouchHelper";

    private Adapter adapter;
    private ViewHolder holder;
    private List<String> data;

    public MyItemTouchHelper(List data,Adapter adapter) {
        this.adapter = adapter;
        this.data = data;
    }

    @Override
    public int getMovementFlags(RecyclerView recyclerView, ViewHolder
            viewHolder) {
        // 拖拽的标记,这里允许上下左右四个方向
        int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT |
                ItemTouchHelper.RIGHT;
        // 滑动的标记,这里允许左右滑动
        int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;
        Log.d(TAG,"getMovementFlags...........");
        return makeMovementFlags(dragFlags, swipeFlags);
    }

    /**
     这个方法会在某个Item被拖动和移动的时候回调,这里我们用来播放动画,当viewHolder不为空时为选中状态
     否则为释放状态
     */
    @Override
    public void onSelectedChanged(ViewHolder viewHolder, int actionState) {
        super.onSelectedChanged(viewHolder, actionState);
        Log.d(TAG, "onSelectedChanged: --------------------------");
        if (viewHolder != null) {
            holder = viewHolder;
            pickUpAnimation(viewHolder.itemView);
        } else {
            if (holder != null) {
                putDownAnimation(holder.itemView);
            }
        }
    }

    @Override
    public boolean onMove(RecyclerView recyclerView, ViewHolder viewHolder,
                          ViewHolder target) {
        // 移动时更改列表中对应的位置并返回true
        int adapterPosition = viewHolder.getAdapterPosition();
        int targetPosition = target.getAdapterPosition();
        Log.d(TAG, "onMoved: "+ adapterPosition +"   "+ targetPosition);
        Collections.swap(data, adapterPosition, targetPosition);
        // 移动完成后刷新列表
        adapter.notifyItemMoved(adapterPosition, targetPosition);
        return true;
    }


    @Override
    public void onSwiped(final ViewHolder viewHolder, int direction) {
        Log.d(TAG, "onSwiped: "+viewHolder.getAdapterPosition()+" "+data.size());
        data.remove(viewHolder.getAdapterPosition());
        // 刷新列表
        adapter.notifyItemRemoved(viewHolder.getAdapterPosition());
    }

    public void pickUpAnimation(View card){
        Log.d(TAG, "pickUpAnimation: -----------------------");
        ObjectAnimator animator = ObjectAnimator.ofFloat(card, "translationZ", 1f, 300f);
        animator.setInterpolator(new DecelerateInterpolator());
        animator.setDuration(300);
        animator.start();
    }
    private void putDownAnimation(View card) {
		ObjectAnimator animator = ObjectAnimator.ofFloat(card, "translationZ", 30f, 1f);
		animator.setInterpolator(new DecelerateInterpolator());
		animator.setDuration(300);
		animator.start();
    }

    public void setDataChang(List data){
        this.data = data;
    }
}

这里有点不好的地方是将数据放在了这里处理,我们知道RecyclerView为了解耦,数据管理是放到了adapter去处理的,所以最好也是在adapter中去处理,可以使用接口的方式来实现,这里只是为了展示方便所以移到一个类中来了。
在使用了ItemTouchHelper时,这里有个坑需要注意,就是不要使用adapter.setHasStableIds(true),要不然会出现各种问题。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值