昨天在改公司项目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 这个接口通过匿名内部类的方式,直接自动重写方法,都是一样的。
在这里可以总结下,在使用recyclerview时什么情况下使用自定义监听,就我遇到的情况来说,大都是需要在外部对子项进行监听时,包括上面提到的拖拽、滑动,以及长按监听等,就是仅仅通过adpter不能达到我们想要的效果。我能想到的就是这样,如果不对,请大神们多多指正。