(内容来自于Android开发艺术探索)
点击事件的分发过程,其实就是MotionEvent事件的发布过程。当事件发生后,系统需要将事件传到一个View上。
事件的传递过程是Activity–>Window–>顶级View(setContentView设置的View)。
分发一个点击事件由三个方法协作完成
1.public boolean dispatchTouchEvent(MotionEvent ev)
事件的发布。返回结果受当前View的onTouchEvent和下级View的dispatchTouchEvent影响。
2.public boolean onInterceptEvent(MotionEvent ev)
在上述方法内部调用,返回结果表示是否拦截次事件,
3.public boolean onTouchEvent(MotionEvent event)
处理该事件,返回结果表示是否消耗该事件,是否处理该事件
例如:当一个事件通过ViewGroup是(事件首先会通过父级容器向下分配),会调用它的dispatchTouchEvent方法,在dispatchTouchEvent方法中,如果 onInterceptEvent方法返回结果为true,则表示事件由它处理,进而会继续调用onTouchEvent方法,如果onInterceptEvent方法返回false,则会传给子view,子view的dispatchEvent方法调用,子view通过onInterceptionEvent方法继续判断是否拦截。
注:当一个View实现了onTouchListener,并且重写了onTouchEvent方法时,事件处理的优先级看onTouchListener中onTouch回调方法的返回值,如果返回false,则代表onTouchListener不处理,交给onTouchEvent方法处理,所以onTouchListener的优先级高于onTouchEvent.
当一个事件产生时,所有子View的onTouchEvent方法都返回false,既不处理该事件,事件最终会回抛给Activity层来处理。(所以源码中,所有View的onTouchEvent事件返回都是false,Activity中返回false)。
事件机制的总结:
1.一系列的事件都是从down—>move..move…–>up
的。称之为一个事件序列
2.一个事件序列理论上只能被一个View拦截
3.一旦拦截一个事件序列中的某一次事件,down—>move..move…–>up之中任何一次事件。所有的事件都会交由该View处理。
4.一旦一个View接收了事件,必须消耗ACTION_DOWN事件(onTouchEvent返回了false就代表不对ACTION_DOWN事件做处理,也代表不对这一事件序列做处理),否则之后的事件都会再返回给父元素,再调用父元素的onTouchEvent方法进行处理,就好比上级交给你做一件事情,开头都没有做好,上级不高兴就不让你做,收回了命令,交给其他人或者自己做了。
例子:下面是一个处理ViewPager滑动事件的例子
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
ViewPager mViewPager = getTouchViewPager(mViewPagers, ev);
Log.i(TAG, "mViewPager = " + mViewPager);
//判断是否拦截此次事件
if (mViewPager != null && mViewPager.getCurrentItem() != 0) {
//默认返回false
return super.onInterceptTouchEvent(ev);
}
switch (ev.getAction()) {
//当只是按下去是,既非滑动事件,不对此事件做处理
case MotionEvent.ACTION_DOWN:
downX = tempX = (int) ev.getRawX();
downY = (int) ev.getRawY();
break;
//当移动时拦截次事件
case MotionEvent.ACTION_MOVE:
int moveX = (int) ev.getRawX();
if (moveX - downX > mTouchSlop
&& Math.abs((int) ev.getRawY() - downY) < mTouchSlop) {
return true;
}
break;
}
return super.onInterceptTouchEvent(ev);
}
onTouchEvent事件的处理
//down事件,因为onTouchEvent返回了true 所以代表处理过了
// up,和move部分进行了分别的处理
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
int moveX = (int) event.getRawX();
int deltaX = tempX - moveX;
tempX = moveX;
if (moveX - downX > mTouchSlop
&& Math.abs((int) event.getRawY() - downY) < mTouchSlop) {
isSilding = true;
}
if (moveX - downX >= 0 && isSilding) {
mContentView.scrollBy(deltaX, 0);
}
break;
case MotionEvent.ACTION_UP:
isSilding = false;
if (mContentView.getScrollX() <= -viewWidth / 2) {
isFinish = true;
scrollRight();
} else {
scrollOrigin();
isFinish = false;
}
break;
}
return true;
}