传递过程遵循如下顺序:Activity->Window->PhoneWindow->DecorView->RootView->ViewGroup->View
View事件方法执行顺序:onTouchListener > onTouchEvent > onLongClickListener > onClickListener
主要由三个重要的方法共同完成的,只有ViewGroup有拦截方法。dispatchTouchEvent onInterceptTouchEvent onTouchEvent
- dispatchTouchEvent:用于进行点击事件的分发
- 如果 return true,事件会分发给当前 View 并由 dispatchTouchEvent 方法进行消费,同时事件会停止向下传递;
- 如果 return false,事件分发分为两种情况:
- 如果当前 View 获取的事件直接来自 Activity,则会将事件返回给 Activity 的 onTouchEvent 进行消费;
- 如果当前 View 获取的事件来自外层父控件,则会将事件返回给父 View 的 onTouchEvent 进行消费。
- 如果返回系统默认的 super.dispatchTouchEvent(ev),事件会自动的分发给当前 View 的 onInterceptTouchEvent 方法。
- onInterceptTouchEvent:用于进行点击事件的拦截
- 如果 onInterceptTouchEvent 返回 true,则表示将事件进行拦截,并将拦截到的事件交由当前 View 的 onTouchEvent 进行处理;
- 如果 onInterceptTouchEvent 返回 false,则表示将事件放行,当前 View 上的事件会被传递到子 View 上,再由子 View 的 dispatchTouchEvent 来开始这个事件的分发;
- return super.onInterceptTouchEvent(ev):默认表示不拦截该事件,并将事件传递给下一层View的dispatchTouchEvent
- onTouchEvent:用于处理点击事件
- 如果事件传递到当前 View 的 onTouchEvent 方法,而该方法返回了 false,那么这个事件会从当前 View 向上传递,并且都是由上层 View 的 onTouchEvent 来接收,如果传递到上面的 onTouchEvent 也返回 false,这个事件就会“消失”,而且接收不到下一次事件。
- 如果返回了 true 则会接收并消费该事件。
- 如果返回 super.onTouchEvent(ev) 默认处理事件的逻辑和返回 false 时相同。
滑动冲突解决:
- 外部拦截法:父View的onInterceptTouchEvent方法中根据业务逻辑需要,在ACTION_MOVE方法中进行判断,如果需要父View处理则返回true,否则返回false,事件分发给子View去处理。
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {boolean intercepted = false;int x = (int) ev.getX();int y = (int) ev.getY();final int action = ev.getAction() & MotionEvent.ACTION_MASK;switch (action) {case MotionEvent.ACTION_DOWN:intercepted = false;//调用ViewPager的onInterceptTouchEvent方法初始化mActivePointerIdsuper.onInterceptTouchEvent(ev);break;case MotionEvent.ACTION_MOVE://横坐标位移增量int deltaX = x - mLastXIntercept;//纵坐标位移增量int deltaY = y - mLastYIntercept;if (Math.abs(deltaX)>Math.abs(deltaY)){intercepted = true;}else{intercepted = false;}break;case MotionEvent.ACTION_UP:intercepted = false;break;default:break;}mLastXIntercept = x;mLastYIntercept = y;LogUtil.e(TAG,"intercepted = "+intercepted);return intercepted;
}
- 内部拦截法:父View不拦截任何事件,所有事件都传递给子View,子View根据需要决定是自己消费事件还是给父View处理。这需要子View使用requestDisallowInterceptTouchEvent方法才能正常工作
//子View
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {int x = (int) ev.getX();int y = (int) ev.getY();final int action = ev.getAction() & MotionEvent.ACTION_MASK;switch (action) {case MotionEvent.ACTION_DOWN:getParent().requestDisallowInterceptTouchEvent(true);break;case MotionEvent.ACTION_MOVE://水平移动的增量int deltaX = x - mLastX;//竖直移动的增量int deltaY = y - mLastY;//当水平增量大于竖直增量时,表示水平滑动,此时需要父View去处理事件if (Math.abs(deltaX) > Math.abs(deltaY)){getParent().requestDisallowInterceptTouchEvent(false);}break;case MotionEvent.ACTION_UP:break;default:break;}mLastX = x;mLastY = y;return super.dispatchTouchEvent(ev);
}//父View
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {final int action = ev.getAction() & MotionEvent.ACTION_MASK;if (action == MotionEvent.ACTION_DOWN){super.onInterceptTouchEvent(ev);return false;}return true;
}