這篇文章會講解RecyclerView的Item拖動排序與滑動刪除。
ItemTouchHelper
This is a utility class to add swipe to dismiss and drag & drop support to RecyclerView.
ItemTouchHelper是一個支持RecyclerView的滑動刪除和拖拽的工具類。也就是說我們可以通過它來簡單的完成RecyclerView的Item拖動排序與滑動刪除。
效果如圖
拖動和滑動.gif
一、使用ItemTouchHelper.Callback
要使用ItemTouchHelper需要創建一個類繼承 ItemTouchHelper.Callback 抽象類,通過這個類可以讓你監聽“move”與 “swipe”事件,而且還可以控制view被選中的狀態以及重寫默認動畫的地方。如果你只是想要一個基本的實現,有一個系統封裝好的可以使用:SimpleCallback。但是為了了解其工作機制,我們還是自己實現。
這是這個類需要些的全部代碼
public class ItemTouchHelperCallback extends ItemTouchHelper.Callback{
private ItemTouchHelperAdapter itemTouchHelperAdapter;//RecyclerView的適配器
/**
* 多參的構造方法
* @param itemTouchHelperAdapter 需要適配器來改變數據的位置,以及通知它來刷新
*/
public ItemTouchHelperCallback(ItemTouchHelperAdapter itemTouchHelperAdapter) {
this.itemTouchHelperAdapter = itemTouchHelperAdapter;
}
/**
* RecyclerView item支持長按進入拖動操作
*/
@Override
public boolean isLongPressDragEnabled() {
return true;
}
/**
* RecyclerView item任意位置觸發啟用滑動操作
*/
@Override
public boolean isItemViewSwipeEnabled() {
return true;
}
/**
* 指定可以支持的拖動和滑動的方向,上下為拖動(drag),左右為滑動(swipe)
*/
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
//判斷布局管理器是否為GridLayoutManager
if (recyclerView.getLayoutManager() instanceof GridLayoutManager || recyclerView.getLayoutManager() instanceof StaggeredGridLayoutManager) {
final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
//不需要滑動
final int swipeFlags = 0;
return makeMovementFlags(dragFlags, swipeFlags);
} else {
final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
final int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;
return makeMovementFlags(dragFlags, swipeFlags);
}
}
/**
* 拖動操作會執行的方法
*/
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
//判斷如果viewHolder類型不一樣,返回False
if (viewHolder.getItemViewType() != target.getItemViewType()) {
return false;
}
//通知適配器改變數據 以及刷新
itemTouchHelperAdapter.onItemMove(viewHolder.getAdapterPosition(), target.getAdapterPosition());
return true;
}
/**
* 滑動操作會執行的方法
*/
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
//通知適配器刪除數據 以及刷新
itemTouchHelperAdapter.onItemDismiss(viewHolder.getAdapterPosition());
}
/**
* 當動作開始的時候調用
*/
@Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
//動作不是空閑狀態調用
if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
if (viewHolder instanceof ItemTouchHelperAdapter.ItemTouchHelperViewHolder) {
//獲取執行的item的ViewHolder
ItemTouchHelperAdapter.ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperAdapter.ItemTouchHelperViewHolder) viewHolder;
//選中狀態回調
itemViewHolder.onItemSelected();
}
}
super.onSelectedChanged(viewHolder, actionState);
}
/**
* 當動作之中的時候調用
*/
@Override
public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
//自定義滑動動畫(讓它根據滑動的距離與itemView的寬度的比例來改變透明度)
final float alpha = 1.0f - Math.abs(dX) / (float) viewHolder.itemView.getWidth();
//設置透明度
viewHolder.itemView.setAlpha(alpha);
//設置平移動畫
viewHolder.itemView.setTranslationX(dX);
} else {
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
}
}
/**
* 當動作結束的時候調用
*/
@Override
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder);
if (viewHolder instanceof ItemTouchHelperAdapter.ItemTouchHelperViewHolder) {
//獲取執行的item的ViewHolder
ItemTouchHelperAdapter.ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperAdapter.ItemTouchHelperViewHolder) viewHolder;
//未選中狀態回調
itemViewHolder.onItemNormal();
}
}
}
- 繼承ItemTouchHelper.Callback的抽象類之后會讓你實現這三個方法
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder)
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target)
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction)
- getMovementFlags在這個方法里面寫通過item的移動方向來判斷是拖動或滑動操作。
- onMove在這個方法里面寫拖動操作后的執行方法,通過適配器來改變數據位置,以及刷新
- onSwiped在這個方法里面寫滑動操作后的執行方法,通過適配器來刪除數據位置,以及刷新
- 之后還需要些兩個輔助動作的方法
@Override
public boolean isLongPressDragEnabled()
@Override
public boolean isItemViewSwipeEnabled()
- isLongPressDragEnabled這個方法默認返回ture,支持長按進入拖動操作
- isItemViewSwipeEnabled這個方法指的是在view任意位置觸摸事件發生時啟用滑動操作
- 如果寫一些在有關操作的動畫,復寫下面三個方法
@Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState)
@Override
public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive)
@Override
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder)
- onSelectedChanged指的是你在開始操作(也就是說選中item)的時候希望做的對item的改變,比如說改變item的背景顏色,還可以通過參數actionState來判斷什么操作
- onChildDraw指的是你在操作(一般指的是滑動)途中希望做的對item的改變,比如寫了一個透明度改變的動畫
- clearView操作結束后的做的對item的改變,讓它恢復原有狀態
二、需要在Adapter寫的方法
全部的代碼
public class ItemTouchHelperAdapter extends RecyclerView.Adapter<ItemTouchHelperAdapter.ItemTouchHelperViewHolder> {
private ArrayList<String> list;//要顯示的數據
private Context context;//上下文
private ImgViewTouchListener imgViewTouchListener;//需要外面實現的接口
/*
多參的構造方法
*/
public ItemTouchHelperAdapter(ArrayList<String> list, Context context) {
this.list = list;
this.context = context;
}
/**
* 創建視圖
*/
@Override
public ItemTouchHelperViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
//創建視圖
View view = LayoutInflater.from(context).inflate(R.layout.item_touch_helper_item,parent,false);
//實例化MainViewHolder---- 傳View過去
ItemTouchHelperViewHolder holder = new ItemTouchHelperViewHolder(view);
return holder;
}
/**
* 初始化控件
*/
public class ItemTouchHelperViewHolder extends RecyclerView.ViewHolder{
public TextView textView;
public ImageView imageView;
public ItemTouchHelperViewHolder(View itemView) {
super(itemView);
//初始化控件
textView = (TextView) itemView.findViewById(R.id.text);
imageView = (ImageView) itemView.findViewById(R.id.imgView);
}
/**
* 操作開始時調用的方法
*/
public void onItemSelected() {
itemView.setBackgroundColor(Color.BLACK);
}
/**
* 操作完成后調用的方法
*/
public void onItemNormal() {
itemView.setBackgroundColor(0);
}
}
/**
* 填充數據
*/
@Override
public void onBindViewHolder(final ItemTouchHelperViewHolder holder, int position) {
//獲取要填充的值
String content = list.get(position);
//控件中設置值
holder.textView.setText(content);
holder.imageView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
imgViewTouchListener.imgViewOnTouch(event,holder);
return false;
}
});
}
/**
* 獲取item的總個數
*/
@Override
public int getItemCount() {
return list.size();
}
/**
* 拖動方法
*/
public boolean onItemMove(int fromPosition, int toPosition) {
//互換列表中指定位置的數據
Collections.swap(list, fromPosition, toPosition);
//通知改變
notifyItemMoved(fromPosition, toPosition);
return true;
}
/**
* 滑動刪除的方法
*/
public void onItemDismiss(int position) {
list.remove(position);
notifyItemRemoved(position);
}
/*
暴露出去的點擊事件的方法
*/
public void setImgViewTouchListener(ImgViewTouchListener imgViewTouchListener){
this.imgViewTouchListener = imgViewTouchListener;
}
/*
點擊要實現的接口
*/
public interface ImgViewTouchListener{
public void imgViewOnTouch(MotionEvent event,ItemTouchHelperViewHolder holder);
}
}
- 寫一個拖動操作調用的方法和一個滑動刪除調用的方法,在上一個類的onMove和onSwiped方法中調用。
- 在ViewHolder內部類寫開始操作和結束操作的方法,在上一個類的onSelectedChanged和clearView方法中調用
- 最后我希望在按下圖片才開始拖拽操作,所有需要寫一個點擊方法(通過上一篇文章的方式)。
三、在Activity中
//實例化ItemTouchHelper對象
final ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new ItemTouchHelperCallback(adapter));
//itemTouchHelper與RecyclerView關聯
itemTouchHelper.attachToRecyclerView(rcTouch);
//設置ImgView的觸摸監聽事件
adapter.setImgViewTouchListener(new ItemTouchHelperAdapter.ImgViewTouchListener() {
@Override
public void imgViewOnTouch(MotionEvent event, ItemTouchHelperAdapter.ItemTouchHelperViewHolder holder) {
//判斷是否按下圖片
if (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_DOWN) {
//按下則開始拖動
itemTouchHelper.startDrag(holder);
}
}
});
- 首先實例化一個ItemTouchHelper類,并且在參數之前寫的ItemTouchHelperCallback類的對象
- 然后將其與RecyclerView關聯
- 設置ImgView的觸摸監聽,在里面通過ItemTouchHelper類來調用開始拖動的方法
RecyclerView的系列的文章
- 第一篇RecyclerView的基本使用 RecyclerView使用(一)
- 第二篇有關多種Item,添加,刪除 RecyclerView使用(二)
- 第三篇RecyclerView的監聽方法RecyclerView使用(三)
- 第四篇RecyclerView的拖動和滑動刪除RecyclerView使用(四)