RecyclerView的item拖动排序效果实现和它的ItemTouchHelper详解

recyclerview这个控件太强大了,自带各种特效,很多高逼格效果需要我们自己简单组合一下就呈现出来,比如拖动排序!它对拓展简直是非常open(OCP原则),废话不扯了,先看效果:
这里写图片描述

说明:
1.实现这个效果的核心类是:ItemTouchHelper和ItemTouchHelper.Callbck.
2.mainActivity的布局就是一个recyclerview,item的布局cardview套着三个控件。

AAA: 首先看MAinActivity的代码吧

public class MainActivity extends AppCompatActivity {


    List<String> list;
    private MyAdapter adapter;
    RecyclerView recyView;

    //初始化集合数据
    {
        list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            list.add("我的序号是:" + i);
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        recyView = (RecyclerView) findViewById(R.id.recyclerview);
        adapter = new MyAdapter();
        recyView.setLayoutManager(new LinearLayoutManager(this));
        recyView.setAdapter(adapter);
        //1.创建item helper
        ItemTouchHelper itemTouchHelper = new ItemTouchHelper(callback);
        //2.绑定到recyclerview上面去
        itemTouchHelper.attachToRecyclerView(recyView);
        //3.在ItemHelper的接口回调中过滤开启长按拖动,拓展其他操作
    }

BBB: 下面是adapter的代码

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {

    List<String> list;
    public MyAdapter(List<String> list) {
        this.list=list;
    }

    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
        MyViewHolder holder = new MyViewHolder(itemView);
        return holder;
    }

    @Override
    public int getItemCount() {
        return list == null ? 0 : list.size();
    }

    @Override
    public void onBindViewHolder(MyViewHolder holder, int position) {
        holder.textNumber.setText(list.get(position));
    }


    class MyViewHolder extends RecyclerView.ViewHolder {

        TextView textNumber;

        public MyViewHolder(View itemView) {
            super(itemView);
            textNumber = (TextView) itemView.findViewById(R.id.textNumber);
        }

    }
}

CCC : 下面是itemTouchHelper的重写代码,并附有大量的注释,我是翻看了源码和谷歌官方文档,写得应该还是比较清楚的了,这个写好的itemTouchHelper代码直接复制过去是可以用的。
注意:目前实现的是item的长按拖动啊!!!

 //itemHelper的回调
    ItemTouchHelper.Callback callback = new ItemTouchHelper.Callback() {

        /**
         * 官方文档的说明如下:
         * o control which actions user can take on each view, you should override getMovementFlags(RecyclerView, ViewHolder)
         * and return appropriate set of direction flags. (LEFT, RIGHT, START, END, UP, DOWN).
         * 返回我们要监控的方向,上下左右,我们做的是上下拖动,要返回都是UP和DOWN
         * 关键坑爹的是下面方法返回值只有1个,也就是说只能监控一个方向。
         * 不过点入到源码里面有惊喜。源码标记方向如下:
         *  public static final int UP = 1     0001
         *  public static final int DOWN = 1 << 1; (位运算:值其实就是2)0010
         *  public static final int LEFT = 1 << 2   左 值是3
         *  public static final int RIGHT = 1 << 3  右 值是8
         */
        @Override
        public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
            //也就是说返回值是组合式的
            //makeMovementFlags (int dragFlags, int swipeFlags),看下面的解释说明
            int swipFlag=0;
            //如果也监控左右方向的话,swipFlag=ItemTouchHelper.LEFT|ItemTouchHelper.RIGHT;
            int dragflag = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
            //等价于:0001&0010;多点触控标记触屏手指的顺序和个数也是这样标记哦
            return  makeMovementFlags(dragflag,swipFlag);

            /**
             * 备注:由getMovementFlags可以联想到setMovementFlags,不过文档么有这个方法,但是:
             * 有 makeMovementFlags (int dragFlags, int swipeFlags)
             * Convenience method to create movement flags.便捷方法创建moveMentFlag
             * For instance, if you want to let your items be drag & dropped vertically and swiped left to be dismissed,
             * you can call this method with: makeMovementFlags(UP | DOWN, LEFT);
             * 这个recyclerview的文档写的简直完美,示例代码都弄好了!!!
             * 如果你想让item上下拖动和左边滑动删除,应该这样用: makeMovementFlags(UP | DOWN, LEFT)
             */

            //拓展一下:如果只想上下的话:makeMovementFlags(UP | DOWN, 0),标记方向的最小值1
        }



        /**
         * 官方文档的说明如下
         * If user drags an item, ItemTouchHelper will call onMove(recyclerView, dragged, target). Upon receiving this callback,
         * you should move the item from the old position (dragged.getAdapterPosition()) to new position (target.getAdapterPosition())
         * in your adapter and also call notifyItemMoved(int, int).
         * 拖动某个item的回调,在return前要更新item位置,调用notifyItemMoved(draggedPosition,targetPosition)
         * viewHolde:正在拖动item
         * target:要拖到的目标
         * @return true 表示消费事件
         */
        @Override
        public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
            //直接按照文档来操作啊,这文档写得太给力了,简直完美!
            adapter.notifyItemMoved(viewHolder.getAdapterPosition(), target.getAdapterPosition());
            //注意这里有个坑的,itemView 都移动了,对应的数据也要移动
            Collections.swap(list, viewHolder.getAdapterPosition(), target.getAdapterPosition());
            return true;
        }



        /**
         * 谷歌官方文档说明如下:
         * 这个看了一下主要是做左右拖动的回调
         * When a View is swiped, ItemTouchHelper animates it until it goes out of bounds, then calls onSwiped(ViewHolder, int).
         * At this point, you should update your adapter (e.g. remove the item) and call related Adapter#notify event.
         * @param viewHolder
         * @param direction
         */
        @Override
        public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
           //暂不处理
        }



        /**
         * 官方文档如下:返回true 当前tiem可以被拖动到目标位置后,直接”落“在target上,其他的上面的tiem跟着“落”,
         * 所以要重写这个方法,不然只是拖动的tiem在动,target tiem不动,静止的
         * Return true if the current ViewHolder can be dropped over the the target ViewHolder.
         * @param recyclerView
         * @param current
         * @param target
         * @return
         */
        @Override
        public boolean canDropOver(RecyclerView recyclerView, RecyclerView.ViewHolder current, RecyclerView.ViewHolder target) {
            return true;
        }


        /**
         * 官方文档说明如下:
         * Returns whether ItemTouchHelper should start a drag and drop operation if an item is long pressed.
         * 是否开启长按 拖动
         * @return
         */
        @Override
        public boolean isLongPressDragEnabled() {
            //return true后,可以实现长按拖动排序和拖动动画了
            return true;
        }
    };

======================================================================================

上面实现item的长按拖动,有很多需求可能触摸item的某个控件就进行拖动。
先看改进后的效果图如下:
这里写图片描述

这个时候去找官方文档了。recyclerview真强大,要啥有啥。
itemTouchHelper有个方法 如下:

        //谷歌官方文档如下:
        //开启拖动我们给他的holder,但是默认
        // 是长按拖动,直接代码调用拖动要先禁止长按拖动
        //Starts dragging the provided ViewHolder. By default,
        // ItemTouchHelper starts a drag when a View is long pressed.
        // You can disable that behavior by overriding isLongPressDragEnabled().
        itemTouchHelper.startDrag(holder);

由于adapter和activity是分离的两个类,所以用了接口回调,更新了adatper的代码和mainActivity的代码。

DDD: 接口的代码如下:

public interface startDragListener {
    //触摸imageview,开启拖动的接口
    void startDragItem(RecyclerView.ViewHolder holder);
}

EEE: 更新后的adapter的代码如下:

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {

    startDragListener draglistener;
    //set接口回调
    public void setDraglistener(startDragListener draglistener) {
        this.draglistener = draglistener;
    }

    List<String> list;
    public MyAdapter(List<String> list) {
        this.list=list;
    }

    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
        MyViewHolder holder = new MyViewHolder(itemView);
        return holder;
    }

    @Override
    public int getItemCount() {
        return list == null ? 0 : list.size();
    }

    @Override
    public void onBindViewHolder(final MyViewHolder holder, int position) {
        holder.textNumber.setText(list.get(position));
        holder.image.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                //注意:这里down和up都会回调该方法
                if (event.getAction()== MotionEvent.ACTION_DOWN) {
                    draglistener.startDragItem(holder);
                }
                return false;
            }
        });
    }


    class MyViewHolder extends RecyclerView.ViewHolder {

        TextView textNumber;
        ImageView image;

        public MyViewHolder(View itemView) {
            super(itemView);
            textNumber = (TextView) itemView.findViewById(R.id.textNumber);
            image= (ImageView) itemView.findViewById(R.id.image);
        }

    }
}

FFF:更新后的mainactivity的代码如下:

public class MainActivity extends AppCompatActivity implements startDragListener{


    List<String> list;
    private MyAdapter adapter;
    RecyclerView recyView;
    private ItemTouchHelper itemTouchHelper;

    //初始化集合数据
    {
        list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            list.add("我的序号是:" + i);
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        recyView = (RecyclerView) findViewById(R.id.recyclerview);
        adapter = new MyAdapter(list);
        adapter.setDraglistener(this);
        recyView.setLayoutManager(new LinearLayoutManager(this));
        recyView.setAdapter(adapter);
        //1.创建item helper
        itemTouchHelper = new ItemTouchHelper(callback);
        //2.绑定到recyclerview上面去
        itemTouchHelper.attachToRecyclerView(recyView);
        //3.在ItemHelper的接口回调中过滤开启长按拖动,拓展其他操作
    }

    //itemHelper的回调
    ItemTouchHelper.Callback callback = new ItemTouchHelper.Callback() {

        /**
         * 官方文档的说明如下:
         * o control which actions user can take on each view, you should override getMovementFlags(RecyclerView, ViewHolder)
         * and return appropriate set of direction flags. (LEFT, RIGHT, START, END, UP, DOWN).
         * 返回我们要监控的方向,上下左右,我们做的是上下拖动,要返回都是UP和DOWN
         * 关键坑爹的是下面方法返回值只有1个,也就是说只能监控一个方向。
         * 不过点入到源码里面有惊喜。源码标记方向如下:
         *  public static final int UP = 1     0001
         *  public static final int DOWN = 1 << 1; (位运算:值其实就是2)0010
         *  public static final int LEFT = 1 << 2   左 值是3
         *  public static final int RIGHT = 1 << 3  右 值是8
         */
        @Override
        public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
            //也就是说返回值是组合式的
            //makeMovementFlags (int dragFlags, int swipeFlags),看下面的解释说明
            int swipFlag=0;
            //如果也监控左右方向的话,swipFlag=ItemTouchHelper.LEFT|ItemTouchHelper.RIGHT;
            int dragflag = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
            //等价于:0001&0010;多点触控标记触屏手指的顺序和个数也是这样标记哦
            return  makeMovementFlags(dragflag,swipFlag);

            /**
             * 备注:由getMovementFlags可以联想到setMovementFlags,不过文档么有这个方法,但是:
             * 有 makeMovementFlags (int dragFlags, int swipeFlags)
             * Convenience method to create movement flags.便捷方法创建moveMentFlag
             * For instance, if you want to let your items be drag & dropped vertically and swiped left to be dismissed,
             * you can call this method with: makeMovementFlags(UP | DOWN, LEFT);
             * 这个recyclerview的文档写的简直完美,示例代码都弄好了!!!
             * 如果你想让item上下拖动和左边滑动删除,应该这样用: makeMovementFlags(UP | DOWN, LEFT)
             */

            //拓展一下:如果只想上下的话:makeMovementFlags(UP | DOWN, 0),标记方向的最小值1
        }



        /**
         * 官方文档的说明如下
         * If user drags an item, ItemTouchHelper will call onMove(recyclerView, dragged, target). Upon receiving this callback,
         * you should move the item from the old position (dragged.getAdapterPosition()) to new position (target.getAdapterPosition())
         * in your adapter and also call notifyItemMoved(int, int).
         * 拖动某个item的回调,在return前要更新item位置,调用notifyItemMoved(draggedPosition,targetPosition)
         * viewHolde:正在拖动item
         * target:要拖到的目标
         * @return true 表示消费事件
         */
        @Override
        public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
            //直接按照文档来操作啊,这文档写得太给力了,简直完美!
            adapter.notifyItemMoved(viewHolder.getAdapterPosition(), target.getAdapterPosition());
            //注意这里有个坑的,itemView 都移动了,对应的数据也要移动
            Collections.swap(list, viewHolder.getAdapterPosition(), target.getAdapterPosition());
            return true;
        }



        /**
         * 谷歌官方文档说明如下:
         * 这个看了一下主要是做左右拖动的回调
         * When a View is swiped, ItemTouchHelper animates it until it goes out of bounds, then calls onSwiped(ViewHolder, int).
         * At this point, you should update your adapter (e.g. remove the item) and call related Adapter#notify event.
         * @param viewHolder
         * @param direction
         */
        @Override
        public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
           //暂不处理
        }



        /**
         * 官方文档如下:返回true 当前tiem可以被拖动到目标位置后,直接”落“在target上,其他的上面的tiem跟着“落”,
         * 所以要重写这个方法,不然只是拖动的tiem在动,target tiem不动,静止的
         * Return true if the current ViewHolder can be dropped over the the target ViewHolder.
         * @param recyclerView
         * @param current
         * @param target
         * @return
         */
        @Override
        public boolean canDropOver(RecyclerView recyclerView, RecyclerView.ViewHolder current, RecyclerView.ViewHolder target) {
            return true;
        }


        /**
         * 官方文档说明如下:
         * Returns whether ItemTouchHelper should start a drag and drop operation if an item is long pressed.
         * 是否开启长按 拖动
         * @return
         */
        @Override
        public boolean isLongPressDragEnabled() {
            //return true后,可以实现长按拖动排序和拖动动画了
            return false;
        }
    };

    @Override
    public void startDragItem(RecyclerView.ViewHolder holder) {
        //谷歌官方文档如下:
        //开启拖动我们给他的holder,但是默认
        // 是长按拖动,直接代码调用拖动要先禁止长按拖动
        //Starts dragging the provided ViewHolder. By default,
        // ItemTouchHelper starts a drag when a View is long pressed.
        // You can disable that behavior by overriding isLongPressDragEnabled().
        itemTouchHelper.startDrag(holder);
    }

}

备注:1.看完这篇文章就可以重写侧滑删除,并有删除动画功能
2.写博客简直太耗费时间了
3.demo源码的下载链接

  • 18
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值