优雅地实现RecycleView的点击、拖动、和侧滑删除

来源:互联网 发布:python 网络拓扑图 编辑:程序博客网 时间:2024/06/05 13:34

优雅地实现RecycleView的点击、拖动、和侧滑删除

自从发布了RecycleView之后,可以很方便的实现列表数据展示,同时只需要指定LayoutManager就可以实现列表、瀑布流、表格布局的无缝切换。RecycleView的所有东西都很美好,但是它没有提供像ListView那样的单击item的接口,都是通过对ViewHolder实现OnclickListener来实现单击事件。这里发现了一种更加优雅的方式,其本质是通过itemonTouchEvent事件来实现的。

点击事件

RecycleView 提供了一个接口叫做addOnItemTouchListener用来为每个item添加触摸事件,实现思路通过触摸手势监听,然后通过触摸坐标判断是哪个item

实现起来很简单:

1、定义一个OnItemClickListener继承自RecycleView.OnItemTouchListener,里面持有一个RecycleView和一个GestureDetectorCompat

public abstract class OnItemClickListener implements RecyclerView.OnItemTouchListener {    private GestureDetectorCompat mGestureDetector;    private RecyclerView recyclerView;    public RecycleItemClickListener(RecyclerView view) {        this.recyclerView = view;        mGestureDetector = new GestureDetectorCompat(recyclerView.getContext(), ItemTouchHelperGestureListener());    }

然后OnItemTouchListener有几个方法需要我们实现。onInterceptTouchEventonTouchEventonRequestDisallowInterceptTouchEvent

我们只需要在onInterceptTouchEventonTouchEvent中把事件交给mGestureDetector处理即可。

  @Override    public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {        mGestureDetector.onTouchEvent(e);        return false;    }    @Override    public void onTouchEvent(RecyclerView rv, MotionEvent e) {        mGestureDetector.onTouchEvent(e);    }

mGestureDetector捕捉到的事件都会交给ItemTouchHelperGestureListener来处理:

 class ItemTouchHelperGestureListener extends GestureDetector.SimpleOnGestureListener {        @Override        public boolean onSingleTapUp(MotionEvent e) {            View child = recyclerView.findChildViewUnder(e.getX(), e.getY());            if (child != null) {                int position = recyclerView.indexOfChild(child);                onItemClick(recyclerView.getChildViewHolder(child), position);            }            return true;        }        @Override        public void onLongPress(MotionEvent e) {            View child = recyclerView.findChildViewUnder(e.getX(), e.getY());            if (child != null) {                int position = recyclerView.indexOfChild(child);                RecycleItemClickListener.this.onLongPress(recyclerView.getChildViewHolder(child), position);            }        }    }

其中onItemClickonLongPressOnItemClickListener提供给外部调用的回调。其声明如下:

    public abstract void onItemClick(RecyclerView.ViewHolder holder, int position);    public void onLongPress(RecyclerView.ViewHolder holder, int position) {}

onLongPress在需要的时候可以重写。

RecycleView使用时是这样的:

   recyclerView.addOnItemTouchListener(new RecycleItemClickListener(recyclerView) {            @Override            public void onItemClick(RecyclerView.ViewHolder holder, int position) {                Toast.makeText(SampleRecycleViewActivity.this, "点击了:" + position, Toast.LENGTH_SHORT).show();            }            @Override            public void onLongPress(RecyclerView.ViewHolder holder, int position) {            }        });

这样就实现了RecycleViewonItemClickListener了。

拖拽移动item

拖拽item的实现需要用到ItemTouchHelper这个接口,这个接口是SDK提供的用于处理拖拽、侧滑删除等功能的。

使用这个东西的步骤非常简单:

  1. 实例化一个ItemTouchHelper
  2. 关联RecycleView
    itemTouchHelper = new ItemTouchHelper(new ItemTouchHelper.Callback());    itemTouchHelper.attachToRecyclerView(recyclerView);

就是这么简单。ItemTouchHelper的构造方法需要一个CallBack对象,这个CallBack对象,需要实现几个方法:getMovementFlagsonMoveonSwiped

其中getMovementFlags的实现如下:

            //设置移动方式            @Override            public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {                final int dragFlags;                final int swipeFlags;                if (recyclerView.getLayoutManager() instanceof GridLayoutManager) {                    dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;                    swipeFlags = 0;                } else {                    dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;                    swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;                }                return makeMovementFlags(dragFlags, swipeFlags);            }

上面代码中,dragFlags是用于拖拽的标志,swipeFlags是滑动标记。上面代码意思是如果是网格布局,拖动方向为上下左右,否则只有上下方向。swipeFlags是用于左右滑动的标志,这里暂且不说。

在设置了dragFlags的时候,就会在长按item的时候进入拖动模式,然后就会一直回调onMove函数。我们可以在onMove中进行数据集的更新:

            //移动过程中调用            @Override            public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {                int fromPosition = viewHolder.getAdapterPosition();                int toPosition = target.getAdapterPosition();                data.add(toPosition, data.remove(fromPosition));                adapter.notifyItemMoved(fromPosition, toPosition);                return false;            }

如果需要在拖拽过程中进行高亮背景切换,可以重写onSelectedChangedclearView方法。

            //当长按的时候调用            @Override            public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {                if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {                    viewHolder.itemView.setBackgroundColor(Color.LTGRAY);                }                super.onSelectedChanged(viewHolder, actionState);            }            //当手指松开的时候调用            @Override            public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {                super.clearView(recyclerView, viewHolder);                viewHolder.itemView.setBackgroundResource(0);            }

如果有需要对某些特定item不能拖动的话,可以重写isLongPressDragEnabled返回false,设置默认不可拖动。然后在前面实现的OnItemClickListeneronLongPress中对需要拖拽的item使用itemTouchHelper.startDrag(holder);就可以拖动了

滑动删除

首先设置标志位:

            //设置移动方式            @Override            public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {                final int dragFlags;                final int swipeFlags;                if (recyclerView.getLayoutManager() instanceof GridLayoutManager) {                    dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;                    swipeFlags = 0;                } else {                    dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;                    swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;                }                return makeMovementFlags(dragFlags, swipeFlags);            }

STARTEND的标志位是指可以左右滑动。

其次在onSwipe方法中进行数据集更新就可以了:

            //侧滑过程中调用            @Override            public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {                int position = viewHolder.getAdapterPosition();                adapter.notifyItemRemoved(position);                data.remove(position);            }

整体效果如下:

ezgif.com-video-to-gif.gif

1 1