从手指触摸到设备屏幕,直至手指离开设备屏幕,这个过程会不断的产生触摸事件,Framework称之为MotionEvent。
分发机制这是用于解决View的嵌套、重叠所产生的触摸事件应该由哪个view来响应的问题;比如:
点击上图中的button,这个点击的事件到底是应该由button响应呢,还是framelayout来响应呢。分发机制就是解决这样的问题的。负责事件分发的方法是dispatchTouchEvent(MotionEvent ev)
分发的过程是:
1.分发事件的起点是触摸事件所在的Activity 调用其dispatchTouchEvent();
2.然后是此Activity的rootView.dispatchTouchEvent();
3.前序遍历rootView的View树,依次调用其ViewGroup(View)的dispatchTouchEvent()
dispatchTouchEvent(MotionEvent ev),所有的View都通过这个方法实现事件的分发,ViewGroup是View的子类,对这个方法进行了覆盖;
View的dispatchTouchEvent的机制是:
1.该方法将返回一个boolean值,默认返回false;其返回true,意味着当前View将消费触摸事件;比如,在Activity中dispatchTouchEvent返回了true,则事件就被分发到Activity,其rootView及rootView下的ViewGroup、View均被屏蔽;
2.dispatchTouchEvent方法内部的逻辑是:
(1)检查当前的View是否注册了onTouchListener
(2)若注册了onTouchListener,则执行onTouchListner.onTouch()方法
(3)若onTouchListner.onTouch()返回了true,表示当前View消费了此次触摸事件,dispatchTouchEvent将返回true;
(4)若未注册onTouchListener,或注册了onTouchListener.onTouch()方法返回了false,则执行onTouchEvent();
(5)我们熟知的onClickListener.onClick()等方法均在onTouchEvent()方法中调用,只要执行了onTouchEvent()方法,则dispatchTouchEvent一定会返回true;
ViewGroup的dispatchTouchEvent的机制是:
1.View的dispatchTouchEvent所均有的特性ViewGroup的dispatchTouchEvent也具有;
2.其逻辑是:
(1)判断ViewGroup是否拦截此次触摸消息,即disallowIntercept||!onInterceptTouchEvent();这个地方就需要插播一下了。
A.分发机制的总体思想是,从层次结构上来说,自上向下的分发,而最底层的View具有最高的优先级去处理触摸事件(因为dispatchTouchEvent默认返回false),只有最底层的View未对触摸事件进行处理(dispatchTouchEvent也返回false),更高层次的ViewGroup才有机会处理触摸事件。
B.这里onInterceptTouchEvent()方法的意思是,拦截触摸事件,它是对上述机制的一个补充,它为高层次的ViewGroup提供了一个屏蔽底层View的方法,只有ViewGroup不拦截触摸事件,其子View才有机会去分发和处理;
C.disallowIntercept这个参数的作用是,否决掉ViewGroup的拦截,这种设计可以使事件的处理更为灵活。这个disallowIntercept参数可以由requestDisallowInterceptTouchEvent()方法来修改。这里需要注意的是,disallowIntercept否决作用的生命周期只是一次触摸事件,如果想要在一系列的戳事件中,都屏蔽掉ViewGorup的拦截,则必须在每次触摸事件分发处理时都要修改disallowIntercept的值;
(2)若ViewGroup拦截了事件,则调用super.dispatchTouchEvent();
(3)若ViewGroup没有拦截事件,则遍历ViewGroup树,在每个ViewGroup或View中调用dispatchTouchEvent;
以上是分发机制的基本内容了,除此之外还有几个问题需要思考:
1.当dispatchTouchEvent在进行事件分发的时候,只有前一个action返回true,才会触发后一个action,我看到有人对这个结论表示不能理解。其实是这样的,比如一个点击动作,至少由两个触摸事件组成:ACTOIN_DOWN,ACTION_UP,前者是按下,后者是提起,考虑ACTION_DOWN被分发到最底层的控件view上,如果对于ACTON_DOWN这个事件,view.dispatchTouchEvent的返回值为false,则说明view对于这个事件“不感兴趣”,那么等到分发ACTION_UP事件时,view的父控件就会认为,既然ACTION_DOWN被view忽略掉,那么ACIONT_UP对于view来说也是没有意义,如果根本不会将ACTION_UP分发给view了。
2.为什么要有onTouch和onTouchEvent()两个方法呢?因为两个方法都是对触摸事件进行处理啊。我觉得可以从这两个方法的加载这个方面来理解,如果要在onTouch()中处理触摸事件,我们需要定义一个onTouchListener,并覆写其onTouch()方法,而onTouchEvent()方法是继承自View类的。如果需要改变控件对于触摸事件处理的特性,onTouch()和onTouchEvent()方法都是可以做到的,区别在于:(1)onTouch()方法是通过接口形式实现的,onTouchEvent()是通过继承实现的;(2)onTouchEvent()是控件的固有属性,这个固有属性可以使得开发人员很方面的使用onClickListener这些框架内的触摸事件处理方式,通时,框架也提供了onTouch()方法,可用于屏蔽掉onTouchEvent()这个控件的固有属性;
总结一下,关于分发机制,最重要的几条:
(1)分发顺序Activity-->rootView->View树遍历
(2)dispatchTouchEvent返回true表示分发给自己,返回false表示“不感兴趣”
(3)dispatchTouchEvent内部先调用onTouch(),再调用onTouchEvent()
(4)ViewGroup通过onInterceptEvent()方法控制拦截,外部可通过disallowIntercept屏蔽ViewGroup的onInterceptEvent()