RecyclerView 之所以比 ListView 好用点,是因为它有一些强大的功能,比如 item 的拖拽或者横滑删除等功能,如果用 ListView 的话,这些需要我们去自定义,但是对于 RecyclerView 来说,系统已经为我们提供了方法,我们只需要按照 API 来调用即可。我们来看看 ItemTouchHelper 这个类。
ItemTouchHelper 从名字中判断,是条目触摸的辅助类,看它的注释说明是辅助item拖拽或滑动的类,我们先来实现一个简单的适配器
public class SwipeDragAdapter extends RecyclerView.Adapter<SwipeDragAdapter.SwipePressViewHolder> {
private Context mContext;
private ArrayList<String> mList;
public SwipeDragAdapter(Context context, ArrayList<String> list) {
this.mContext = context;
this.mList = list;
}
@Override
public SwipePressViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.item_swipe_press_rv, viewGroup, false);
return new SwipePressViewHolder(view);
}
@Override
public void onBindViewHolder(final SwipePressViewHolder holder, int position) {
String url = mList.get(position);
holder.tvContent.setText(position + " " + url);
}
@Override
public int getItemCount() {
int count = 0;
if (mList != null && !mList.isEmpty()) {
count = mList.size();
}
return count;
}
class SwipePressViewHolder extends RecyclerView.ViewHolder {
private TextView tvContent;
private ImageView ivDrag;
SwipePressViewHolder(View itemView) {
super(itemView);
tvContent = (TextView) itemView.findViewById(R.id.tv_swipe_press);
ivDrag = (ImageView) itemView.findViewById(R.id.iv_drag_swipe_press);
}
}
}
item 的 xml 布局
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv_swipe_press"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignBottom="@+id/iv_drag_swipe_press"
android:layout_marginEnd="16dp"
android:layout_marginStart="16dp"
android:gravity="center_vertical"
android:lines="2"
android:textColor="@color/color_3"
android:textSize="14sp" />
<ImageView
android:id="@+id/iv_drag_swipe_press"
android:layout_width="56dp"
android:layout_height="56dp"
android:layout_alignParentRight="true"
android:contentDescription="@null"
android:padding="16dp"
android:src="@mipmap/ic_launcher"
/>
</RelativeLayout>
Activity 中
String[] arr = {
" 1 ",
" 2 ",
" 3 ",
" 4 ",
" 5 ",
" 6 ",
" 7 ",
" 8 ",
" 9 ",
" 10 ",
" 11 ",
" 12 ",
" 13 ",
" 14 ",
" 15 "
};
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(linearLayoutManager);
ArrayList<String> list = new ArrayList<>(Arrays.asList(arr));
SwipeDragAdapter swipeDragAdapter = new SwipeDragAdapter(this, list);
recyclerView.setAdapter(swipeDragAdapter);
这样,就出现了一个简单的列表。继续,ItemTouchHelper 中有对外暴露的回调,那么我们就写一个类来实现它的回调
public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback {
public SimpleItemTouchHelperCallback() {
}
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
return 0;
}
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
return false;
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
}
}
实现默认的方法,我们看看这三个方法的名字及功能,getMovementFlags() 是获取移动标识的,我们可以在这个里面控制拖拽滑动的方向,比如说允许上下拖拽,不允许左右拖拽;onMove() 标识是否拖拽成功,可以在返回true的同时,对外暴露回调方法,我们在里面完成自己的逻辑操作;onSwiped() 这个很明显就是左右滑动的方法,我们可以在这个方法中对外暴露回调,删除集合中的元素,刷新界面。
除了上述三个方法,还有几个也要注意, isLongPressDragEnabled() 方法表示是否允许拖拽,这个是开关,默认为 true,只有为true才可以拖拽;isItemViewSwipeEnabled()方法表示是否允许横滑,使用方法同上;onSelectedChanged() 方法是在 item 被拖拽或滑动时调用;clearView() 方法对应 onSelectedChanged() 方法,它是指手指抬起时调用。
getMovementFlags() 中获取值,使用位运算的方式来记录数据的,ItemTouchHelper 本身提供了方向的int值,然后通过 makeMovementFlags() 方法来进行合并,比如说我们想让item可以上下拖拽,左右滑动移除,则可以设置为
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
int dragFlag = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
int swipeFlag = ItemTouchHelper.START | ItemTouchHelper.END;
return makeMovementFlags(dragFlag, swipeFlag);
}
makeMovementFlags(int dragFlags, int swipeFlags) 方法中两个形参的名字,我们也可以看出来,一个是拖拽的flag,一个是横滑的flag。
同时,为了在拖动时有回调以便于我们做自己的逻辑,所以定义个回调接口
public interface OnItemTouchListener {
boolean onItemMove(int fromPosition, int toPosition);
void onSwipeDismiss(int position);
}
SimpleItemTouchHelperCallback 中重写拖拽和横滑的方法
private OnItemTouchListener adapter;
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
if (viewHolder.getItemViewType() != target.getItemViewType())
return false;
adapter.onItemMove(viewHolder.getAdapterPosition(), target.getAdapterPosition());
return true;
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
adapter.onSwipeDismiss(viewHolder.getAdapterPosition());
}
同时 SwipeDragAdapter 实现 OnItemTouchListener 接口,写入自己的逻辑
@Override
public boolean onItemMove(int fromPosition, int toPosition) {
Collections.swap(mList, fromPosition, toPosition);
notifyItemMoved(fromPosition, toPosition);
return true;
}
@Override
public void onSwipeDismiss(int position) {
mList.remove(position);
notifyItemRemoved(position);
}
在 Activity 中,把 Adapter 和 ItemTouchHelper 及 RecyclerView 关联起来,
ItemTouchHelper.Callback itemTouchHelperCallback = new SimpleItemTouchHelperCallback(swipeDragAdapter);
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(itemTouchHelperCallback);
itemTouchHelper.attachToRecyclerView(recyclerView);
这样,就可以实现我们想要的功能了。
此时,如果我们按下item,左右滑动时,item会随着手指移动,加入自己的逻辑,划出后会把该条目删除掉;长按item,上下滑动时,item会被拖拽的离开原先的位置,我们需要在对应的回调中调整集合中两个元素的位置,同时刷新界面;这里注意一点,必须是长按item才可以拖拽,如果我们想在item上添加一个图标,按下图标就能拖拽item,这个功能可以实现吗?如果了解 ItemTouchHelper 源码,就会发现,其实拖拽的起始于一个方法 startDrag() ,这个方法注释里也有用法说明
/*
* viewHolder.dragButton.setOnTouchListener(new View.OnTouchListener() {
* public boolean onTouch(View v, MotionEvent event) {
* if (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_DOWN) {
* mItemTouchHelper.startDrag(viewHolder);
* }
* return false;
* }
* });
*/
如果我们不想在适配器中调用,可以再定义一个接口做回调处理;同理,左右滑动事件也有相应的方法 startSwipe(),用法相同。如果我们想在item拖动时改变写item的颜色背景,停止拖动时恢复原状,我们也可以定义回调来实现逻辑,下面给一个完整点的代码。
public interface ItemTouchViewHolder {
void onItemSelected();
void onItemClear();
}
public interface StartDragListener {
void onStartDrag(RecyclerView.ViewHolder holder);
}
public class SwipeDragAdapter extends RecyclerView.Adapter<SwipeDragAdapter.SwipePressViewHolder>
implements OnItemTouchListener {
private Context mContext;
private ArrayList<String> mList;
private StartDragListener mDragListener;
public SwipeDragAdapter(Context context, ArrayList<String> list) {
this.mContext = context;
this.mList = list;
}
public void setDragListener(StartDragListener dragListener) {
this.mDragListener = dragListener;
}
@Override
public SwipePressViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View view = LayoutInflater.from(viewGroup.getContext())
.inflate(R.layout.item_swipe_press_rv, viewGroup, false);
return new SwipePressViewHolder(view);
}
@Override
public void onBindViewHolder(final SwipePressViewHolder holder, int position) {
String url = mList.get(position);
holder.tvContent.setText(position + " " + url);
//拖动图标设置OnTouch接口使其具有和长按item拖动一样的效果
holder.ivDrag.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
if (mDragListener != null &&
MotionEventCompat.getActionMasked(motionEvent) == MotionEvent.ACTION_DOWN)
mDragListener.onStartDrag(holder);
return false;
}
});
}
@Override
public int getItemCount() {
int count = 0;
if (mList != null && !mList.isEmpty()) {
count = mList.size();
}
return count;
}
@Override
public boolean onItemMove(int fromPosition, int toPosition) {
Collections.swap(mList, fromPosition, toPosition);
notifyItemMoved(fromPosition, toPosition);
return true;
}
@Override
public void onSwipeDismiss(int position) {
mList.remove(position);
notifyItemRemoved(position);
}
class SwipePressViewHolder extends RecyclerView.ViewHolder implements ItemTouchViewHolder {
private TextView tvContent;
private ImageView ivDrag;
SwipePressViewHolder(View itemView) {
super(itemView);
tvContent = (TextView) itemView.findViewById(R.id.tv_swipe_press);
ivDrag = (ImageView) itemView.findViewById(R.id.iv_drag_swipe_press);
}
@Override
public void onItemSelected() {
tvContent.setTextColor(ContextCompat.getColor(mContext, R.color.colorAccent));
}
@Override
public void onItemClear() {
tvContent.setTextColor(ContextCompat.getColor(mContext, R.color.color_3));
}
}
}
public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback {
private final static String TAG = "TouchHelperCallback";
private final float ALPHA = 1.0f;
private OnItemTouchListener adapter;
private boolean isLongPressEnable = true;
private boolean isSwipeEnable = true;
public SimpleItemTouchHelperCallback(OnItemTouchListener adapter) {
this.adapter = adapter;
}
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
int dragFlag = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
int swipeFlag = ItemTouchHelper.START | ItemTouchHelper.END;
return makeMovementFlags(dragFlag, swipeFlag);
}
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
if (viewHolder.getItemViewType() != target.getItemViewType())
return false;
adapter.onItemMove(viewHolder.getAdapterPosition(), target.getAdapterPosition());
return true;
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
adapter.onSwipeDismiss(viewHolder.getAdapterPosition());
}
@Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
if (actionState == ItemTouchHelper.ACTION_STATE_DRAG
&& viewHolder instanceof ItemTouchViewHolder) {
((ItemTouchViewHolder) viewHolder).onItemSelected();
}
super.onSelectedChanged(viewHolder, actionState);
}
@Override
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
if (viewHolder instanceof ItemTouchViewHolder) {
((ItemTouchViewHolder) viewHolder).onItemClear();
}
super.clearView(recyclerView, viewHolder);
}
@Override
public boolean isLongPressDragEnabled() {
return isLongPressEnable;
}
@Override
public boolean isItemViewSwipeEnabled() {
return isSwipeEnable;
}
}
Activity 中调用
SwipeDragAdapter swipeDragAdapter = new SwipeDragAdapter(this, list);
recyclerView.setAdapter(swipeDragAdapter);
ItemTouchHelper.Callback itemTouchHelperCallback = new SimpleItemTouchHelperCallback(swipeDragAdapter);
final ItemTouchHelper itemTouchHelper = new ItemTouchHelper(itemTouchHelperCallback);
itemTouchHelper.attachToRecyclerView(recyclerView);
swipeDragAdapter.setDragListener(new StartDragListener() {
@Override
public void onStartDrag(RecyclerView.ViewHolder holder) {
itemTouchHelper.startDrag(holder);
}
});