项目地址
首先,本文不会涉及完全理论性的关于事件分发的讲解,而是将一部分理论应用到该实例中。因为事件分发理论太多、太复杂,如果不是处理很复杂的自定义View,只需要用到其中一小部分就可以完成任务了,对此,我们必须:
从理论中来,到实践中去
为什么重写dispatchTouchEvent()
在写自定义View的时候,一旦涉及到事件分发,往往会重写onTouchEvent(),而这里却是重写了dispatchTouchEvent(),为什么呢?要回答这个问题,就必须说明dispatchTouchEvent()是干什么的。
当事件到达View时,如果他是个普通的View,则没有必要去分发事件,直接调用到onTouchEvent来决定是否要处理该事件;如果他是个ViewGroup,那么他就必须调用dispatchTouchEvent()把事件分发下去,只有当ViewGroup主动拦截了事件,或者所有的子孩子都不处理这个事件,该事件才会传递到他的onTouchEvent()方法。
在该例子中,PtrFrameLayout必须决定事件是自己处理(调用scroll方法),还是传递给子孩子(调用super.dispatchTouchEvent()。试想,如果我们重写的是onTouchEvent,而且content是一个ScrollView,那么所有的事件都会被ScrollView拿到(除非拦截,那样又增加了复杂度)。所以,必须在分发的时候就要做出判断。
DOWN事件的处理
问题:为什么不能直接返回super.dispatchTouchEvent()?
如果content是原生控件,没有设置clickListener,也没有重写ouTouch等,反正就是他不会处理任何事件。如普通的LinearLayout,他会所有事件的处理都是返回false,也就是告诉父控件--事件给你吧,我统统不要。那么,PtrFrameLayout的dispatchTouchEvent()也就返回false,也就是告诉他的父控件,后面的事件我也不care了,你就不要传递给我了。所以,后面的MOVE事件他也就接受不到了,此时控件也就滑不动了。
问题:为什么要把DOWN事件传递给子视图?
如果一个视图没有拿到DOWN事件,那么后续的事件他也就都拿不到了。因为父视图不把DOWN事件往下传递,也就是说,后面的事件我自己都要了。
问题:正确的处理方式是怎样?
首先,我们要确保PtrFrameLayout无论如何都要拿到事件,对于子孩子能处理的事件,给子孩子处理,而子孩子不能处理的,则自己处理。所以,在DOWN事件中,事件要先交给子视图,无论子视图怎么处理,父视图返回true,表示事件已经处理,那么后续的MOVE事件还是会先到父视图来,而且当父视图再次调用super.dispatchTouchEvent()把消息往下传递时,子视图也能收到MOVE事件,从而实现content(如ScrollView)的滑动。
MOVE事件的处理
在滑动过程中,有时候是content滑动,有时候是PtrFrameLayout调用scroll方法执行滑动。如果想让content滑动,直接调用super.dispatchTouchEvent(),事件会传递给content,他会自己处理事件;如果想让PtrFrameLayout滑动,调用scroll方法后,返回true即可,代表事件已经处理。