有时候我们可能需要实现侧滑删除的功能,又或者长按Item进行拖动与其他Item进行位置的交换,但RecyclerView没有提供现成的API供我们操作,但是SDK提供了ItemTouchHelper这样一个工具类帮助我们快速实现以上功能。首先我们来看下使用recyclerView的上下拖拽和侧滑删除效果,完整项目:
官方提供了ItemTouchHelper类使用步骤如下:
定义ItemTouchHelper.Callback实现类,以下是几个重要的方法我们在使用ItemTouchHelper
时,必须自定义一个ItemTouchHelper.Callback
,我们来了解一下其中比较重要的几个方法。
在此方法里面我们需要构建两个flag,一个是dragFlags,表示拖动效果支持的方向,另一个是swipeFlags,表示侧滑效果支持的方向。在我们的Demo中,拖动执行上下两个方向,侧滑执行左右两个方向,这些操作我们都可以在此方法里面定义。
onMove当拖动效果已经产生了,会回调此方法。在此方法里面,我们通常会更新数据源,就比如说,一个ItemView
从0拖到了1位置,那么对应的数据源也需要更改位置。onSwiped当侧滑效果以上产生了,会回调此方法。在此方法里面,我们也会更新数据源。与onMove
方法不同到的是,我们在这个方法里面从数据源里面移除相应的数据,然后调用notifyXXX
方法就行了。 onSelectedChanged
在每次View Holder的状态变成拖拽 (ACTION_STATE_DRAG) 或者 滑动 (ACTION_STATE_SWIPE)的时候被调用。
clearView
在一个view被拖拽然后被放开的时候被调用,
import android.graphics.Canvas;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView;
public class MessageItemTouchCallback extends ItemTouchHelper.Callback {
private static final String TAG = "ItemTouchCallback";
private ItemTouchHelperAdapterCallback adapterCallback;
public MessageItemTouchCallback(ItemTouchHelperAdapterCallback adapterCallback) {
this.adapterCallback = adapterCallback;
}
@Override
public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
//callback回调监听哪些动作?---判断方向
//表示拖动效果支持的方向
int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
//表示侧滑效果支持的方向
int swipeFlags = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
return makeMovementFlags(dragFlags, swipeFlags);
}
@Override
public boolean onMove(RecyclerView arg0, RecyclerView.ViewHolder srcHolder, RecyclerView.ViewHolder targetHolder) {
// 监听滑动(水平方向、垂直方向)
//让数据集合中的两个数据进行位置交换
//同时还要刷新RecyclerView
adapterCallback.onItemMove(srcHolder.getAdapterPosition(), targetHolder.getAdapterPosition());
return true;
}
@Override
public void onSwiped(RecyclerView.ViewHolder holder, int arg1) {
// 滑动动作的时候回调
//1.删除数据集合里面的position位置的数据
//2.刷新adapter
adapterCallback.onItemSwiped(holder.getAdapterPosition());
}
@Override
public void onChildDraw(Canvas c, RecyclerView recyclerView,
RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState,
boolean isCurrentlyActive) {
Log.d(TAG, "onChildDraw");
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState,
isCurrentlyActive);
}
//滑动消失的距离,当滑动小于这个值的时候会删除这个item,否则不会视为删除
//返回值作为用户视为拖动的距离
@Override
public float getSwipeThreshold(RecyclerView.ViewHolder viewHolder) {
return 0.1f;
}
//返回值滑动消失的距离,滑动小于这个值不消失,大于消失
@Override
public float getSwipeEscapeVelocity(float defaultValue) {
return 5f;
}
//设置手指离开后ViewHolder的动画时间
@Override
public long getAnimationDuration(RecyclerView recyclerView, int animationType, float animateDx, float animateDy) {
return 100;
}
//网格型RecyclerView
@Override
public float getMoveThreshold(RecyclerView.ViewHolder viewHolder) {
return 0.9f;
}
//返回值决定是否有滑动操作
@Override
public boolean isItemViewSwipeEnabled() {
return true;
}
}
声明ItemTouchHelper,并绑定到待管理的RecyclerView上
ItemTouchHelper.Callback callback = new MessageItemTouchCallback(adapter);
itemTouchHelper = new ItemTouchHelper(callback);
itemTouchHelper.attachToRecyclerView(recyclerView);
在Adapter中定义滑动删除和拖动排序的数据逻辑
@Override
public boolean onItemMove(int fromPosition, int toPosition) {
//让数据集合中的两个数据进行位置交换
Collections.swap(list, fromPosition, toPosition);
//同时还要刷新RecyclerView
// notifyDataSetChanged();这种会刷新整个adapter不推荐使用
notifyItemMoved(fromPosition, toPosition);
return false;
}
@Override
public void onItemSwiped(int adapterPosition) {
//1.删除数据集合里面的position位置的数据
list.remove(adapterPosition);
//2.刷新adapter
notifyItemRemoved(adapterPosition);
}
这样一个拖动重排的功能就实现了。完整代码见:https://github.com/buder-cp/CustomView/tree/master/buder_DN_view/buderdn11