實現RecyleView中使用ItemTouchHelper實現拖拽和側滑刪除效果,實現步驟分析如下:
效果圖:
一、創建ItemTouchHelper對象
ItemTouchHelper.Callback callback = new ItemTouchHelperCallBack(adapter);
//條目觸摸輔助類
itemTouchHelper = new ItemTouchHelper(callback);
itemTouchHelper.attachToRecyclerView(recyleview);
ItemTouchHelper屬於android.support.v7.widget.helper中的類,我們來創建ItemTouchHelper實例,發現需要一個Callback,該Callback是ItemTouchHelper.Callback屬於ItemTouchHelper內部類;
接着我們就需要新建一個類比如ItemTouchHelperCallback繼承自ItemTouchHelper.Callback,itemTouchHelper.attachToRecyclerView(recyleview)方法將ItemTouchHelper與recyleview進行綁定,然後通過監聽recyleview條目的上下左右觸摸滑動童工callback回調監聽方法,所以callback就是個中間接口;
繼承自ItemTouchHelper.Callback需要重寫其相關方法,ItemTouchHelper.Callback必需要重寫的幾個方法。
1、public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder)
該方法主要定義返回可以滑動的方向,比如說允許從右到左側滑,允許上下拖動等。通過源碼註釋可以看到可以使用makeMovementFlags(int,int)或makeFlag(int, int)來構造返回值。
2、public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target)
查看源碼註釋中的內容:Called when ItemTouchHelper wants to move the dragged item from its old position to the new position.
當條目觸發移動拖拽時會回調改方法。
3、public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction)
查看源碼註釋內容:Called when a ViewHolder is swiped by the user.
當條目左右滑動的時候觸發
getMovementFlags方法中定義好觸摸滑動的方向代碼如下:
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
int dragFlag = ItemTouchHelper.DOWN | ItemTouchHelper.UP;
int swipeFlag = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
int flag = makeMovementFlags(dragFlag,swipeFlag);
return flag;
}
這樣就可以觸發長按拖拽效果和觸摸左右滑動效果,但是不會真正的換位置和刪除條目,還需要自己實現數據源的刷新業務。觸發這個效果也是有兩個默認方法起到作用
4、public boolean isLongPressDragEnabled():
該方法返回true時,表示支持長按拖動,即長按ItemView後纔可以拖動,我們遇到的場景一般也是這樣的。默認是返回true。
5、public boolean boolean isItemViewSwipeEnabled():
該方法返回true時,表示如果用戶觸摸並左右滑動了View,那麼可以執行滑動刪除操作,即可以調用到onSwiped()方法。默認是返回true。
二、拖拽效果觸發
如果要主動觸發上下拖拽換位置的效果,例如,條目中觸摸頭像實現上下拖拽互換位置的效果,需要用到一個方法:itemTouchHelper.startDrag(viewHolder);來主動觸發條目的拖拽效果
1、在Adapter中添加頭像的觸摸拖拽事件
adapter中添加頭像的觸摸事件:
holder.ivHead.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN){
if(dragListener != null){
dragListener.startDrag(holder);
}
}
return false;
}
});
adapter由於需要activity中的itemTouchHelper通信,兩個類建立聯繫最好的方式就是通過接口,需要定義一個接口StartDragListener 讓activity實現該方法,並在new adapterStartDragListener)對象傳入,activity實現接口方法,並觸發itemTouchHelper.startDrag(viewHolder);方法。
RVTouchActivity中實現接口方法:
@Override
public void startDrag(RecyclerView.ViewHolder viewHolder) {
itemTouchHelper.startDrag(viewHolder);
}
StartDragListener 接口定義:
public interface StartDragListener {
void startDrag(RecyclerView.ViewHolder viewHolder);
}
2、實現拖拽移動位置和側滑刪除功能
要真正實現拖拽後移動位置效果,需要用到adapter.notifyItemMoved(startPosition,tagetPosition);而需要觸發移動拖拽的方法是onMove方法,所以需要在onMove方法中觸發adapter.notifyItemMoved(startPosition,tagetPosition),這裏又是兩個類之間通信,最好的方法還是定義接口,通過接口觸發,如下是onMove和onSwiped方法的實現,我們都通過moveListener接口來觸發
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
if(viewHolder.getItemViewType() != target.getItemViewType()){
return false;
}
if(moveListener != null){
moveListener.onItemMove(viewHolder.getAdapterPosition(),target.getAdapterPosition());
}
return true;
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
if(moveListener != null){
moveListener.onItemReMove(viewHolder.getAdapterPosition());
}
}
ItemTouchMoveListener 接口定義:
public interface ItemTouchMoveListener {
void onItemMove(int startPosition,int tagetPosition);//拖拽方法
void onItemReMove(int adapterPosition);//移除方法
}
adapter中實現該接口,並實現接口方法:
由於adapter實現了ItemTouchMoveListener 接口,可以通過如下方法將接口傳給ItemTouchHelperCallBack
ItemTouchHelper.Callback callback = new ItemTouchHelperCallBack(adapter);
@Override
public void onItemMove(int startPosition, int tagetPosition) {
Collections.swap(data,startPosition,tagetPosition);//數據源排序
this.notifyItemMoved(startPosition,tagetPosition);//刷新視圖
}
@Override
public void onItemReMove(int adapterPosition) {
data.remove(adapterPosition);//數據源移除條目數據
this.notifyItemRemoved(adapterPosition);//刷新視圖
}
三、側滑動畫以及選中背景效果
ItemTouchHelper.Callback中還有一些方法,可以實現相應的效果
1、public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState):
從靜止狀態變爲拖拽或者滑動的時候會回調該方法,參數actionState表示當前的狀態。
2、public void clearView(RecyclerView recyclerView, ViewHolder viewHolder):
當用戶操作完畢某個item並且其動畫也結束後會調用該方法,一般我們在該方法內恢復ItemView的初始狀態,防止由於複用而產生的顯示錯亂問題。
3、public void onChildDraw(…):
我們可以在這個方法內實現我們自定義的交互規則或者自定義的動畫效果。
具體實現如下:
@Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
if(actionState!=ItemTouchHelper.ACTION_STATE_IDLE){
viewHolder.itemView.setBackgroundColor(
viewHolder.itemView.getContext()
.getResources()
.getColor(R.color.colorAccent));
}
super.onSelectedChanged(viewHolder, actionState);
}
@Override
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
viewHolder.itemView.setBackgroundColor(Color.WHITE);
viewHolder.itemView.setAlpha(1);
viewHolder.itemView.setScaleX(1);
viewHolder.itemView.setScaleY(1);
super.clearView(recyclerView, viewHolder);
}
@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){
float alpha = 1-Math.abs(dX)/viewHolder.itemView.getWidth();
viewHolder.itemView.setAlpha(alpha);//1~0
viewHolder.itemView.setScaleX(alpha);//1~0
viewHolder.itemView.setScaleY(alpha);//1~0
}
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
}
源碼地址:https://github.com/heiyl/recyleview
圖注:關注公衆號