View事件的分发机制由三个方法共同完成,这三个方法是:
public boolean dispatchTouchEvent(MotionEvent ev);
public boolean onInterceptTouchEvent(MotionEvent ev);
public boolean onTouchEvent(MotionEvent ev);
它们之间的关系可以用下面的伪代码表示:
public boolean dispatchTouchEvent(MotionEvent ev) { boolean consume=false; if(onInterceptTouchEvent(ev)) { consume=onTouchEvent(ev); } else { consume=child.dispatchTouchEvent(ev); } return consume; }
从上面的伪代码中可以看出点击事件的传递规则是这样的:
点击事件的传递是由外向内的,最先接受的到事件的是最外层的ViewGroup,这时候会调用dispatchTouchEvent方法(如果能够接收到事件,那么此方法一定会被调用),如果onInterceptTouchEvent方法返回的是true,那么就说明它要拦截这个事件,于是这个事件就交给该ViewGroup处理,也就是调用onTouchEvent方法。
相反的,如果onInterceptTouchEvent的方法返回的是false,那么就说明该ViewGroup不拦截事件,事件会传递给它的子元素,子元素的dispatchTouchEvent方法会被调用,以此类推一直进行下去,直到事件最终被处理。
如果该事件已经交给子view处理,但是子view的onTouchEvent方法返回了false(没有处理事件,或者处理事件失败了),那么父容器的onTouchEvent就会被调用(我们可以形象的认为下级处理事件失败,那么就由上级进行处理),以此类推上去,如果所有的元素都不处理这个事件,那么这个事件最终会传递给activity,activity的onTouchEvent方法会被调用。
事件分发的时候传递顺序为Activity->Window->View.
由上面的分析过程我们可以知道上面三个方法分别的作用是什么:
dispatchTouchEvent:是用来分发事件的,如果事件能够传递到该view,那么该view的dispatchTouchEvent方法一定会被调用。
onInterceptTouchEvent:用来判断是否拦截事件,返回true表示拦截,返回false表示不拦截。一旦事件被该view拦截,那么同一个事件序列中该方法不会再被调用。
onTouchEvent:用来处理事件,返回true表示消耗,false表示不消耗。如果不消耗,那么同一个事件序列中当前的view无法再接收到事件。
何为同一个事件序列:手指触摸屏幕的那一刻到手指离开屏幕的那一刻,这个过程中产生的一系列事件。
(例如当你在滑动的时候:ACTION_DOWN->ACTION_MOVE->ACTION_MOVE...->ACTION_UP)
onTouchEvent,OnTouchListener,OnClickListener的优先级:
OnTouchListener>onTouchEvent>OnClickListener
如何说呢:如果该view处理事件的时候设置了OnTouchListener,那么OnTouchListener方法里面的onTouch会被回调,若是onTouch返回了true,那么onTouchEvent不会被调用,反之onTouchEvent会被调用,此时若是设置了OnClickListener,那么在onTouchEvent中onClick方法会被调用。
有几个需要注意的小点:
1、一个view一旦开始处理事件,若它不消耗ACTION_DOWN事件(onTouchEvent返回了false),那么同一个事件序列的其他事件都不会再交给它处理,事件会交到父元素去处理(父元素的onTouchEvent会被调用)。若是该view消耗了ACTION_DOWN事件,但是不消耗其他事件,那么事件就会消失,最终这些消失的点击事件就会传递给Activity处理。
2、ViewGroup默认不拦截所有的事件,它的onInterceptTouchEvent默认返回了false。
3、view没有onInterceptTouchEvent方法,一旦有事件传递给它,那么它的onTouchEvent方法就会被带调用。
4、view的onTouchEvent方法默认返回true,也就会说view默认会消耗事件,除非它是不了点击的,也就是它的clickable和longClickable同时被设置为false。事实上view的longClickable属性默认为false,clickable则要分情况,比如Button的clickable属性默认为true,TextView的clickable属性默认为false。
5、view的enable属性不会影响onTouchEvent的默认返回值。即使其为disable状态,只要它的clickable或者longClickable有一个为true,那么它的onTouchEvent就返回true。
6、onClick会发生的前提是当前的View是可点击的,并且它收到了down和up事件。