這篇文章分析這3個類
這3個類是和清單item的drag和swipe手勢有關的類,通過這3個類可實作類似dragsortlistview的功能。
先看下用法
ItemTouchHelper.Callback callback = new SimpleItemTouchHelperCallback(adapter);
mItemTouchHelper = new ItemTouchHelper(callback);
mItemTouchHelper.attachToRecyclerView(recyclerView);
附上高手寫的demo連結
https://github.com/iPaulPro/Android-ItemTouchHelper-Demo
其中SimpleItemTouchHelperCallback是繼承ItemTouchHelper.Callback
那我們從attachToRecyclerView這個方法開始分析
public void attachToRecyclerView(RecyclerView recyclerView) {
if (mRecyclerView == recyclerView) {
return; // nothing to do
}
if (mRecyclerView != null) {
destroyCallbacks();
}
mRecyclerView = recyclerView;
if (mRecyclerView != null) {
setupCallbacks();
}
}
最後是調用setupCallbacks進行一些初始化工作
private void setupCallbacks() {
ViewConfiguration vc = ViewConfiguration.get(mRecyclerView.getContext());
mSlop = vc.getScaledTouchSlop();
mRecyclerView.addItemDecoration(this);//增加ItemDecoration監聽
mRecyclerView.addOnItemTouchListener(mOnItemTouchListener);//item swipe手勢監聽
mRecyclerView.addOnChildAttachStateChangeListener(this);//子view attach和dettach的監聽
initGestureDetector();//初始化drag手勢監聽
}
一、初始化這些監聽和ItemTouchHelper内部的一個類息息相關,那就是ItemTouchHelper.Callback,這個Callback
1.初始化sUICallback
static {
if (Build.VERSION.SDK_INT >= 21) {
sUICallback = new ItemTouchUIUtilImpl.Lollipop();
} else if (Build.VERSION.SDK_INT >= 11) {
sUICallback = new ItemTouchUIUtilImpl.Honeycomb();
} else {
sUICallback = new ItemTouchUIUtilImpl.Gingerbread();
}
}
ItemTouchUIUtilImpl主要有一下這些方法
1.1onDraw 設定 view 的x、y軸的偏移量
1.2onSelected 不同的系統對應不同的實作
1.3clearView 則是清除 onDraw 、onSelected 做的事情
2.設定哪些類型的手勢是enabled
例如:
isItemViewSwipeEnabled 是否可以swipe操作
isLongPressDragEnabled 是否可以drag操作
可以繼承ItemTouchHelper.Callback複寫這兩個方法控制這兩個手勢是否enable
3.控制drag和swipe手勢的方向,例如上下的是drag行為,左右的是swipe行為
getMovementFlags 子類重寫,控制每個手勢的方向
makeMovementFlags 在一個int值上儲存兩個手勢相關的值,類似android view繪制中makemesure方法
4.當子view被drag時,有兩個方法相關
4.1會回調onMove方法,不過是個空實作
4.2當onMove傳回true時,會回調onMoved,這個方法能確定被drag的view不會離開recyclerview的邊界
5.控制哪些子view可以拖動
5.1canDropOver 空實作
5.2chooseDropTarget 這個方法主要是判斷被drag的view應不應該替換被覆寫的那個view,他們倆是否該交換位置
6.當子view處在swipe手勢之後
6.1onSwiped 是個空實作,使用者重寫可以對adapter的資料進行操作
二、mOnItemTouchListener
這個監聽是用來分發drag和swipe事件的
2.1mOnItemTouchListener中 mGestureDetector.onTouchEvent(event); 這句代碼是将事件傳遞給ItemTouchHelperGestureListener.onLongPress()方法
在初始化的時候這個方法就是drag的監聽
private void initGestureDetector() {
if (mGestureDetector != null) {
return;
}
mGestureDetector = new GestureDetectorCompat(mRecyclerView.getContext(),
new ItemTouchHelperGestureListener());
}
2.2
if (index >= 0) {
checkSelectForSwipe(action, event, index);
}
這段代碼就是分發swipe事件的
三、真正執行drag和swipe手勢的方法
其實無論drag是swipe,最後都要經過select方法,隻不過傳的參數不同
1.drag
select(viewHolder, ACTION_STATE_DRAG);
2.swipe
select(vh, ACTION_STATE_SWIPE);
那接下來分析下select方法裡做了啥
關鍵代碼這一句
mRecyclerView.invalidate();
調用這句代碼後會通知RecyclerView 的OnDraw方法
@Override
public void onDraw(Canvas c) {
super.onDraw(c);
final int count = mItemDecorations.size();
for (int i = 0; i < count; i++) {
mItemDecorations.get(i).onDraw(c, this, mState);
}
}
最後還是會CallBack裡的onDraw方法
最後總結一下select方法的調用流程,
ItemTouchHelperGestureListener.onLongPress-->select-->Callback.onDraw-->Callback.onChildDraw-->ItemTouchUIUtilImpl.onDraw
select方法其實隻是控制了子view的一個偏移位置 ,Callback裡的其他方法也有各自調用的時機。看Callback方法說明對應的去看就能理順ItemTouchHelper裡面所做的事情