关于View事件分发机制的文章已经有很多了,推荐郭霖和鸿洋的两篇文章,
http://blog.csdn.net/guolin_blog/article/details/9097463
http://blog.csdn.net/lmj623565791/article/details/38960443
结合他们写的,自己简单总结一下,可能只适用个人。
只要你触摸到了任何一个控件,就一定会调用该控件的dispatchTouchEvent方法,源码如下(最新的API源码已经不是这样了,但是分析依然可行)
public boolean dispatchTouchEvent(MotionEvent event) {
```
if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
mOnTouchListener.onTouch(this, event)) {
return true;
}
return onTouchEvent(event);
}
从这个方法可以看出,当控件setOnTouchListener
后整个事件分发的流程如下:
View.dispatchEvent -> OnTouchListener.onTouch -> View.onTouchEvent
其中dispatchTouchEvent
是当我们对View执行动作,如按下、移动、抬起等动作,系统调用的;onTouch
就是程序员写的事件监听器setOnTouchListener(new OnTouchListener(){...})
当我们在onTouch
方法里面返回true
时,这时dispatchTouchEvent
方法就会返回true
而不会把事件分发到onTouchEvent
了,而onClick
方法、onLongClick
方法正是在onTouchEvent
方法里面调用的,所以只有在onTouch
方法里面返回了false
,才有可能触发onClick
、onLongClick
方法。
观察onTouchEvent
方法的源码(为了不占篇幅,直接从上面的文章看)我们可以知道:
①如果一个View是enable和clickable的时候,这个onTouchEvent
一定会返回true
,这个结论有什么用呢,当dispatchTouchEvent
在进行事件分发的时候,只有这个方法返回true
,才会触发下一个动作的事件。所以当onTouch
方法返回false
,事件会分发到onTouchEvent
,如果View是enable和clickable的,必然会返回true
,这样其他动作的事件才能继续触发dispatchTouchEvent
。假如我们自己写一个自定义控件去继承Button,重写dispatchTouchEvent
方法,将这个方法返回false
时。我们点击这个Button,事件在执行完按下
这个操作后就被消费了,当你抬起
手指时系统也不会调用再dispatchTouchEvent
了!
②当处在MotionEvent.ACTION_UP
时,如果系统判定之前处在MotionEvent.ACTION_DOWN
执行过长按
操作,即调用了下面这个performLongClick
方法(其实内部就是回调OnLongClickListener
的onLongClick
方法),并且返回的是false
,才能触发onClick
。否则onLongClick
方法返回true
,mHasPerformedLongPress
就变为true
,就无法执行
performClick
方法了。
if (performLongClick()) {
mHasPerformedLongPress = true;
}
if (!mHasPerformedLongPress) {
// This is a tap, so remove the longpress check
removeLongPressCallback();
// Only perform take click actions if we were in the pressed state
if (!focusTaken) {
// Use a Runnable and post this rather than calling
// performClick directly. This lets other visual state
// of the view update before click actions start.
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {
performClick();
}
}
}
当你操作一个控件时,不管是按下、移动、抬起,在事件中途未被消费的情况下系统会按照View.dispatchEvent -> OnTouchListener.onTouch -> View.onTouchEvent
分发下去。
如,按下
控件时产生一个MotionEvent.ACTION_DOWN
事件,系统首先将事件分发到dispatchTouchEvent
,然后将事件分发到程序员写的onTouch
方法,
onTouch
方法返回了true
,dispatchTouchEvent
则返回true
,事件不会传递到onTouchEvent
;onTouch
方法返回了false
,dispatchTouchEvent
将会调用onTouchEvent
方法,在这个方法内处理各种逻辑。 长按
操作,将回调程序员写的长按方法,如果程序员写的onLongClick
方法返回true
,将使得长按标志位为true
。false
时,才会回调onClick
方法,默认情况下长按标志位为false
。dispatchTouchEvent
方法返回false
,则无法触发其他动作的事件,这里要分清事件是否会触发以及事件是否会分发。执行完按下
动作后执行抬起
动作,系统产生MotionEvent.ACTION_UP
事件,则又按上述事件分发流程走一遍。
原文地址:http://blog.csdn.net/leelit/article/details/46482909