0x000 前言
現在幾乎養成了一種固定的寫作模式 , 在開始正文之前 ,總想嘮嗑幾句 , 或技術簡介亦或是近來的所思所想 , 都希望有個記錄 , 與人分享交流 。這是一個開放的時代 , 我們很幸運的生活在這個時代 , 這么一個開放的時代 , 互聯網將世界各地的知識信息傳輸到我們面前 , 幾乎零距離 。然而 , 信息爆炸也成其了我們的不幸 , 每天被各種信息淹沒 , 我們如一葉扁舟 , 在信息的大海里 , 如履薄冰 。
0x001 ItemTouchHelper
在 AndroidUI初探③RecyclerView之ItemDecoration中 , 介紹了RecyclerView Item之間怎樣處理Style , ItemDecoration給了我們更多的想象 , 我們可以通過getItemOffsets
來設置偏移量 , onDraw
來繪制各種圖形 , 可以通過Drawable來打造漂亮的ItemDecoration 。
如果說ItemDecoration是裝飾Item的 , 那么ItemTouchHelper就是Item與用戶交互的利器 。那么ItemTouchHelper是個什么樣的類呢?他會為我們做些什么樣的事情 。
ItemTouchHelper簡介
源碼中的解釋是這樣的 :
This is a utility class to add swipe to dismiss and drag & drop support to RecyclerView.
大致說的是:這是一個RecyclerView的工具,提供了drag & swipe 的功能,也就是說 , 這個類可以幫助我們處理RecyclerView中Item的Drag和Swipe
It works with a RecyclerView and a Callback class, which configures what type of interactions are enabled and also receives events when user performs these actions.
大致說的是:他需要RecyclerView 還有ItemTouchHelper.CallBack配合使用,可以配置交互類型 , 并可以接收用戶操作的事件 。也就是說 , 將ItemTouchHelper與RecyclerView關聯之后 , CallBack可以接收到用戶操作RecyclerView的事件 , 這樣我們就可以做一些交互處理 。
英語渣渣 , 英語好的請忽略翻譯
0x002 ItemTouchHelper的使用
一 , 創建ItemHelper對象
// 創建ItemTouchHelper的時候 , 就會要求我們傳入CallBack , 用作RecyclerView事件的回調 , 并做出處理 。
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(callBack);
// 關聯RecylerView
itemTouchHelper.attachToRecyclerView(rvSimpleList);
二 , 實現CallBack
class ItemTouchHelperCallback extends ItemTouchHelper.Callback {
/**
* 需要處理的方向 , 如上下移動 , 左右滑動等
* @param recyclerView
* @param viewHolder
* @return
*/
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
return 0;
}
/**
* 移動時會回調
* @param recyclerView
* @param viewHolder
* @param target
* @return
*/
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
return false;
}
/**
* 滑動時會回調
* @param viewHolder
* @param direction
*/
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
}
}
只需兩步 , 我們就可以將RecyclerView的事件接收掉 。那么他是怎么接收的呢 ? 我們深入源碼看一看 。
0x003 ItemTouchHelper源碼簡析
在分析源碼的時候 , 通常源碼都會比較多 , 所以我們要找準主旨 , 我們是要分析ItemHelper是怎樣將RecyclerView的事件接收起來的,那么我們主要看RecyclerView的事件處理,在ItemTouchHelper中是怎樣體現的。
首先 , 從ItemTouchHelper關聯RecyclerView方法看起 , attachToRecyclerView(@Nullable RecyclerView recyclerView)
這樣方法就是我們ItemTouchHelper關聯RecyclerView的開始 。
public void attachToRecyclerView(@Nullable RecyclerView recyclerView) {
if (mRecyclerView == recyclerView) {
return; // nothing to do
}
if (mRecyclerView != null) {
destroyCallbacks();
}
mRecyclerView = recyclerView;
if (mRecyclerView != null) {
final Resources resources = recyclerView.getResources();
mSwipeEscapeVelocity = resources
.getDimension(R.dimen.item_touch_helper_swipe_escape_velocity);
mMaxSwipeVelocity = resources
.getDimension(R.dimen.item_touch_helper_swipe_escape_max_velocity);
setupCallbacks();
}
}
得到了RecyclerView對象 , 接著就是將RecyclerView的TouchListener給搶過來 。進入setupCallbacks()
我們可以看到 , 在ItemTouchHelper中設置的了RecyclerView的addOnItemTouchListener
并且@Override
了onInterceptTouchEvent(RecyclerView recyclerView, MotionEvent event)
and onTouchEvent(RecyclerView recyclerView, MotionEvent event)
and onRequestDisallowInterceptTouchEvent(boolean disallowIntercept)
.
onInterceptTouchEvent
和onTouchEvent
主要是將RecyclerView的TouchListener事件傳遞給ItemTouchHelper.Callback并做了相應的處理, 這樣我們就可以在ItemTouchHelper的CallBack只關心我們想要處理的 。
事件傳遞代碼塊
if (mSelected == null) {
final RecoverAnimation animation = findAnimation(event);
if (animation != null) {
mInitialTouchX -= animation.mX;
mInitialTouchY -= animation.mY;
endRecoverAnimation(animation.mViewHolder, true);
if (mPendingCleanup.remove(animation.mViewHolder.itemView)) {
// 事件傳遞
mCallback.clearView(mRecyclerView, animation.mViewHolder);
}
// 主要交互處理
select(animation.mViewHolder, animation.mActionState);
updateDxDy(event, mSelectedFlags, 0);
}
}
0x004 ItemTouchHelper簡單示例
public class DragItemTouchHelperCallback extends ItemTouchHelper.Callback {
private final ViewHolderDragSwipeItemListener dragSwipeItemListener;
public DragItemTouchHelperCallback(ViewHolderDragSwipeItemListener dragSwipeItemListener) {
this.dragSwipeItemListener = dragSwipeItemListener;
}
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
/*拖拽方向*/
int dragFlag = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
/*滑動方向*/
int swipeFlag = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
int flags = makeMovementFlags(dragFlag, swipeFlag);
return flags;
}
/**
* 是否開啟長按拖拽 ,必須開啟
* @return
*/
@Override
public boolean isLongPressDragEnabled() {
return true;
}
/**
* 上下移動交換Item
* @param recyclerView
* @param viewHolder
* @param target
* @return
*/
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
if (dragSwipeItemListener != null) {
dragSwipeItemListener.onDragMoveSwap(viewHolder.getAdapterPosition(),target.getAdapterPosition());
}
return true;
}
/**
* 左右滑動刪除
* @param viewHolder
* @param direction
*/
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
if (dragSwipeItemListener != null) {
dragSwipeItemListener.onSwipeDelete(viewHolder.getAdapterPosition());
}
}
/**
* 選中Item狀態
* @param viewHolder
* @param actionState Item狀態
*/
@Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
if (actionState == ItemTouchHelper.ACTION_STATE_IDLE) {
return;
}
viewHolder.itemView.setBackgroundColor(Color.GRAY);
super.onSelectedChanged(viewHolder, actionState);
}
/**
* ItemView復位回調
* @param recyclerView
* @param viewHolder
*/
@Override
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder);
viewHolder.itemView.setBackgroundColor(Color.WHITE);
// Item會復用 , 所有操作ItemView的狀態之后一定要復原
viewHolder.itemView.setAlpha(1);
viewHolder.itemView.setScaleX(1);
viewHolder.itemView.setScaleY(1);
}
/**
* 操作Item的時候會不斷調用繪制Item , 我們可以在這里做很多事情。
* @param c
* @param recyclerView
* @param viewHolder
* @param dX
* @param dY
* @param actionState
* @param isCurrentlyActive
*/
@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.setScaleX(value);
viewHolder.itemView.setScaleY(value);
}
}
}
0x005 效果圖
0x006 The end
高手 , 都是通過不斷的練習 。雖然有些事物看起簡單 , 但深入其中 ,自會發現 , 每一件事都不是獨立而存在 , 都是一環扣著一環 , 不要被表象迷惑 , 要敢于深究其理 。
源碼 : AdvancedUI