这篇文章分析这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里面所做的事情