常见场景浅析
为了方便大家更好的吸收本篇博客的知识,先描述一个常见场景帮大家理一理事件分发流程。这个场景大家肯定有见过:给RecyclerView的Item设置一个点击事件,点击这个Item通常会有两种情形:
- 快速点击,直接触发Item的点击事件
- 手指按到这个Item然后开始滑动,这个时候RecyclerView跟着手指开始滑动了
场景1 我们可以简单理解为:快速点击的时候,由于Item设置有点击事件,导致View的onTouchEvent默认返回true,然后滑动距离又小于设备的scaledTouchSlop(最小滑动距离),所以触发了点击事件。
场景2 我们手指按下去的时候,明明ItemView在ACTION_DOWN的时候已经把事件消费了(返回了true),后面的事件流程又是什么样的呢?这个时候我们猜测:刚开始ACTION_DOWN事件是传递给了ItemView,后续的ACTION_MOVE事件,由于滑动距离大于了scaledTouchSlop(最小滑动距离), RecyelerView又将事件拦截下来了,接着后续事件就交由RecyclerView来处理了。为了印证我们的猜测,老规矩还是从源码中找答案,首先我们分析下RecyclerView的onInterceptTouchEvent方法
//RecyclerView.java
...
//0
private int mScrollState = SCROLL_STATE_IDLE;
...
@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
...
//1
final boolean canScrollHorizontally = mLayout.canScrollHorizontally();
final boolean canScrollVertically = mLayout.canScrollVertically();
...
switch (action) {
...
case MotionEvent.ACTION_MOVE: {
...
final int x = (int) (e.getX(index) + 0.5f);
final int y = (int) (e.getY(index) + 0.5f);
//2
if (mScrollState != SCROLL_STATE_DRAGGING) {
final int dx = x - mInitialTouchX;
final int dy = y - mInitialTouchY;
boolean startScroll = false;
//2
if (canScrollHorizontally && Math.abs(dx) > mTouchSlop) {
mLastTouchX = x;
startScroll = true;
}
//3
if (canScrollVertically && Math.abs(dy) > mTouchSlop) {
mLastTouchY = y;
startScroll = true;
}
//4
if (startScroll) {
setScrollState(SCROLL_STATE_DRAGGING);
}
}
} break;
...
}
//5
return mScrollState == SCROLL_STATE_DRAGGING;
}
复制代码
下面我们整理一下上面代码的逻辑:
- 标注5处的代码是onInterceptTouchEvent的返回值,由于mScrollState初始值是SCROLL_STATE_IDLE,所以我们可以得知RecyclerView在接受到ACTION_DOWN事件的时候并没有拦截事件(这是一句废话,如果ACTION_DOWN事件都拦截的话,那么所有自己的子view都拿不到触摸事件了),这也是为啥ACTION_DOWN的时候ItemVi