前言
ItemTouchHelper是一个RecyclerView的工具类,目的是为简化RecyclerView拖拽、侧滑功能的开发工作。网络搜索可以找到其具体用法。因为ItemTouchHelper默认是长按之后才能拖拽RecyclerView中的item,并且不能通过配置改变这种情况,所以我这里跟大家分享一下实现短按item拖拽的方式。
分析源码
RecyclerView版本:24.0.0
ItemTouchHelper的用法一般如下:
ItemTouchHelper helper = new ItemTouchHelper(new Callback());
helper.attachToRecyclerView(rv);
其attachToRecyclerView方法实现如下,其中我们要关注的点是对setupCallbacks方法的调用。
public void attachToRecyclerView(@Nullable RecyclerView recyclerView) {
...
setupCallbacks();
...
}
setupCallbacks方法实现如下,其中调用了initGestureDetector方法,看来ItemTouchHelper是通过GestureDetector来实现拖拽功能的。
private void setupCallbacks() {
...
initGestureDetector();
}
initGestureDetector实现如下,确实是通过GestureDetector实现的,我们来看看ItemTouchHelperGestureListener中对事件的处理。
private void initGestureDetector() {
if (mGestureDetector != null) {
return;
}
mGestureDetector = new GestureDetectorCompat(mRecyclerView.getContext(),
new ItemTouchHelperGestureListener());
}
ItemTouchHelperGestureListener 实现如下,直接在onLongPress方法中去处理拖拽事件。
private class ItemTouchHelperGestureListener extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onDown(MotionEvent e) {
return true;
}
@Override
public void onLongPress(MotionEvent e) {
...
}
}
一路看来可以判断出,我们无法通过代码设置来实现短按拖拽,只能通过非寻常方法来实现了。
实现方法
其实最简单的一种方法就是直接把ItemTouchHelper类复制一份,将OnLongPress中的代码复制到OnShowPress方法中去就可以实现了,但是这样做会造成代码的冗余,违背了程序设计的避免重复原则,所以我此处不采用此方法。
另一种方法就是采用反射去改变长按短按的时间,不过这两个值是在GestureDetector中的,而且是常量,无法修改,所以只能略过了。
第三种方法是将ItemTouchHelper中的GestureDetector的监听器给取出来,然后设置一个自定义的监听器进去,在自定义的监听器的短按事件onShowPress中去调用原监听器的长按方法onLongPress。我的实现代码如下,其中mDoDrag变量是为不让整个itemView都能拖拽 而加进去的,如果不需要可以删去:
public class ListenerInterceptor {
private GestureDetector.OnGestureListener mListener2BeIntercept;
private InterceptListener mListener2Intercept;
private boolean mDoDrag = true;
public ListenerInterceptor(ItemTouchHelper helper) {
mListener2Intercept = new InterceptListener();
try {
Field fGesDetector = ItemTouchHelper.class.getDeclaredField("mGestureDetector");
fGesDetector.setAccessible(true);
Object objGesDetector = fGesDetector.get(helper);
Field fImpl = GestureDetectorCompat.class.getDeclaredField("mImpl");
fImpl.setAccessible(true);
Object objImpl = fImpl.get(objGesDetector);
Field fLis = null;
try {
fLis = objImpl.getClass().getDeclaredField("mListener");
}catch (Exception e){e.printStackTrace();}
Object oDet = null;
if (fLis == null){
Field fDet = objImpl.getClass().getDeclaredField("mDetector");
fDet.setAccessible(true);
oDet = fDet.get(objImpl);
fLis = oDet.getClass().getDeclaredField("mListener");
}
fLis.setAccessible(true);
mListener2BeIntercept = (GestureDetector.OnGestureListener) fLis.get(oDet);
fLis.set(oDet, mListener2Intercept);
} catch (Exception e) {
e.printStackTrace();
}
}
public void setDoDrag(boolean drag){
mDoDrag = drag;
}
public class InterceptListener extends GestureDetector.SimpleOnGestureListener {
@Override
public void onShowPress(MotionEvent e) {
if (mDoDrag)
mListener2BeIntercept.onLongPress(e);
}
}
}