优雅地实现RecycleView的点击、拖动、和侧滑删除
自从发布了RecycleView
之后,可以很方便的实现列表数据展示,同时只需要指定LayoutManager
就可以实现列表、瀑布流、表格布局的无缝切换。RecycleView
的所有东西都很美好,但是它没有提供像ListView
那样的单击item
的接口,都是通过对ViewHolder
实现OnclickListener
来实现单击事件。这里发现了一种更加优雅的方式,其本质是通过item
的onTouchEvent
事件来实现的。
点击事件
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
有几个方法需要我们实现。onInterceptTouchEvent
、onTouchEvent
、onRequestDisallowInterceptTouchEvent
我们只需要在onInterceptTouchEvent
和onTouchEvent
中把事件交给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);
}
}
}
其中onItemClick
和onLongPress
是OnItemClickListener
提供给外部调用的回调。其声明如下:
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) {
}
});
这样就实现了RecycleView
的onItemClickListener
了。
拖拽移动item
拖拽item的实现需要用到ItemTouchHelper
这个接口,这个接口是SDK提供的用于处理拖拽、侧滑删除等功能的。
使用这个东西的步骤非常简单:
- 实例化一个
ItemTouchHelper
- 关联
RecycleView
itemTouchHelper = new ItemTouchHelper(new ItemTouchHelper.Callback());
itemTouchHelper.attachToRecyclerView(recyclerView);
就是这么简单。ItemTouchHelper
的构造方法需要一个CallBack
对象,这个CallBack
对象,需要实现几个方法:getMovementFlags
、onMove
、onSwiped
。
其中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;
}
如果需要在拖拽过程中进行高亮背景切换,可以重写onSelectedChanged
和clearView
方法。
//当长按的时候调用
@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
,设置默认不可拖动。然后在前面实现的OnItemClickListener
的onLongPress
中对需要拖拽的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);
}
START
和END
的标志位是指可以左右滑动。
其次在onSwipe
方法中进行数据集更新就可以了:
//侧滑过程中调用
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
int position = viewHolder.getAdapterPosition();
adapter.notifyItemRemoved(position);
data.remove(position);
}
整体效果如下: