昨天在改公司项目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()方法中返回接口方法。
在fragment实现此接口,重写onItemMove()方法(回调?)
可以看到在这个方法中实现了对拖拽子项的监听,当检测到拖拽动作时,做了交换数据并更换item位置等操作。
然后实例化了一个viewItemTouchHelper对象,创建一个itemTouchHelper对象,最后和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);
}
}
1.定义一个接口ItemSwipeListener,在接口中定义方法onItemSwiped();
2.在RecyclerViewItemTouchHelper类中声明接口,属性写入构造函数,并在onSwiped()中调用接口方法;
3.在fragment中实现接口,并重写接口方法,通过构造函数实例化RecyclerViewItemTouchHelper。
其实这里第二点,也可以通过写个setListener方法将实例传进来,adapter.setXXXListener()然后new 这个接口通过匿名内部类的方式,直接自动重写方法,都是一样的。
简言之,A想让C帮忙做事情,但是C需要A的工具(数据),于是有一个中间人B接口,B声明了A想做的事情(抽象方法),A将工具通过B来拿给C(调用接口),C只需要实现B中声明的方法(接口回调)即可。(不知道对不对)
在这里可以总结下,在使用recyclerview时什么情况下使用自定义监听,就我遇到的情况来说,大都是需要在外部对子项进行监听时,包括上面提到的拖拽、滑动,以及长按监听等,就是仅仅通过adpter不能达到我们想要的效果。我能想到的就是这样,如果不对,请大神们多多指正。