本文主要分析RecyclerView的onInterceptTouchEvent()对三种事件怎么处理的
这里只放了一些比较重要点的代码,有一部分省略
先看onInterceptTouchEvent()的返回值
return mScrollState == SCROLL_STATE_DRAGGING
解释一下mScrollState这个变量,代表RecyclerView的滑动状态,它一共有三种值
public static final int SCROLL_STATE_IDLE = 0; // 滑动停止
public static final int SCROLL_STATE_DRAGGING = 1; //正在被用户滑动
public static final int SCROLL_STATE_SETTLING = 2;//正在自动滑动,这个大家日常应该都遇到过,当我们快速滑动然后抬起手时会发生自动滑动
ACTION_DOWN
@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
...
switch (action) {
case MotionEvent.ACTION_DOWN:
...
if (mScrollState == SCROLL_STATE_SETTLING) {
getParent().requestDisallowInterceptTouchEvent(true);
setScrollState(SCROLL_STATE_DRAGGING);
stopNestedScroll(TYPE_NON_TOUCH);
}
...
break;
...
}
return mScrollState == SCROLL_STATE_DRAGGING;
}
down事件主要看中间的判断部分,如果当前的滑动状态是自动滑动(SCROLL_STATE_SETTLING)状态,则将滑动状态置为正在滑动(SCROLL_STATE_DRAGGING),然后停止滑动,这样就对down事件做了拦截,其实很好理解:当你的列表在自动快速滚动的过程中,手指再按上去,是需要停止滑动的;
所以只有这种情况RecyclerView才会拦截down事件,其他情况分发给子view
ACTION_MOVE
case MotionEvent.ACTION_MOVE: {
...
final int x = (int) (e.getX(index) + 0.5f);
final int y = (int) (e.getY(index) + 0.5f);
if (mScrollState != SCROLL_STATE_DRAGGING) {
final int dx = x - mInitialTouchX;
final int dy = y - mInitialTouchY;
boolean startScroll = false;
if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
mLastTouchX = x;
startScroll = true;
}
if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
mLastTouchY = y;
startScroll = true;
}
if (startScroll) {
setScrollState(SCROLL_STATE_DRAGGING);
}
}
} break;
move事件处理就很好理解了,如果当前滚动状态是正在滑动,就直接拦截;如果不是,则判断x轴和y轴上滑动距离是否超过最小滑动距离,超过了就将滚动状态置为正在滑动状态,然后拦截;
ACTION_UP
case MotionEvent.ACTION_UP: {
mVelocityTracker.clear();
stopNestedScroll(TYPE_TOUCH);
} break;
up事件除了停止滑动什么也没干,如果当前是正在滑动状态直接拦截,否则分发子view
总结
RecyclerView对三种事件的处理:
down事件:如果当前处于自动滚动状态,则拦截事件,停止滚动,否则分发给子view
move事件:如果当前处于正在滑动状态直接拦截,否则判断水平与竖直滑动距离是否超过最小滑动距离(mTouchSlop),超过则拦截,否则不拦截
up事件:只做了停止滑动操作,是否拦截依赖当前滑动状态