View的事件传递

一直对view之间的手势事件传递机制模模糊糊,今天来好好理理,首先说说一些关于事件的一些基础知识

事件类型MotionEvent

ACTION_DOWN, ACTION_UP, ACTION_MOVE, ACTION_POINTER_DOWN, ACTION_POINTER_UP, ACTION_CANCEL等等,并且每个事件都是以ACTION_DOWN开始ACTION_UP结束的

事件处理分类

  1. 事件分发

    public boolean dispatchTouchEvent(MotionEvent ev)
    

    只要你触摸了任何控件,就一定会调用该控件的dispatchTouchEvent方法

  2. 事件拦截

    public boolean onInterceptTouchEvent(MotionEvent ev)
    

    只有ViewGroup才有onInterceptTouchEvent方法,因为一个普通的View肯定是位于最深层的View,touch事件能够传到这里已经是最后一站了

  3. 事件响应

    public boolean onTouchEvent(MotionEvent ev)
    

事件传递流程

(1) 事件从Activity.dispatchTouchEvent()开始传递,只要没有被停止或拦截,从最上层的View(ViewGroup)开始一直往下(子View)传递。子View可以通过onTouchEvent()对事件进行处理。

(2) 事件由父View(ViewGroup)传递给子View,ViewGroup可以通过onInterceptTouchEvent()对事件做拦截,停止其往下传递。

(3) 如果事件从上往下传递过程中一直没有被停止,且最底层子View没有消费事件,事件会反向往上传递,这时父View(ViewGroup)可以进行消费,如果还是没有被消费的话,最后会到Activity的onTouchEvent()函数。

事件的层级传递

我们都知道如果给一个控件注册了touch事件,每次点击它的时候都会触发一系列的ACTION_DOWN,ACTION_MOVE,ACTION_UP等事件。这里需要注意,如果你在执行ACTION_DOWN的时候返回了false,后面一系列其它的action就不会再得到执行了。简单的说,就是当dispatchTouchEvent在进行事件分发的时候,只有前一个action返回true,才会触发后一个action。

底层的View如何阻止父层的View截获touch事件

方法一:重写底层view的dispatchTouchEvent方法

@Override    
public boolean dispatchTouchEvent(MotionEvent ev) {   
    getParent().requestDisallowInterceptTouchEvent(true);  
    return super.dispatchTouchEvent(ev);    
}  

方法二:当用户按下的时候,我们告诉父组件,不要拦截我的事件(这个时候子组件是可以正常响应事件的),拿起之后就会告诉父组件可以阻止。

public boolean onTouch(View v, MotionEvent event) {
     switch (event.getAction()) {
         case MotionEvent.ACTION_MOVE: 
             myView.requestDisallowInterceptTouchEvent(true);
         break;
         case MotionEvent.ACTION_UP:
         case MotionEvent.ACTION_CANCEL:
             myView.requestDisallowInterceptTouchEvent(false);
         break;
     }
}

最后在网上找了两张流程图,觉得表达的很好,也一起分享一下:

  1. View不处理事件流程图

    view-touch-1

  2. View处理事件流程图

    view-touch-2