前言
在自定义组件的时候少不了会去处理一些事件相关的东西,关于事件这块网上有很多文章,有说的对的也有说的不对的,我在理解的时候也有过一段时间的迷惑,现在把自己理解的东西写下来,给有相同疑问的朋友提供些思路。
事件呢,分为两个大的链条,一个是事件分发链条,一个是事件处理链条。分发链条是由外至内,也就是从父到子,父节点先收到并有权决定是否继续分发;而事件处理链条则是由内至外,即先由最内部的子节点处理事件,子节点有权决定是由自己来处理事件,还是抛给父节点。
概念解释
onTouchEvent
Implement this method to handle touch screen motion events.
此接口是用来处理事件的,如果返回true则代表已处理,返回false则代表未处理
onInterceptTouchEvent
Implement this method to intercept all touch screen motion events。
此接口是用来拦截事件的,如果返回true则代表已拦截,那么就不会再向子对象传递(本身的onTouchEvent函数依然可以处理),返回false,代表未拦截。
This allows you to watch events as they are dispatched to your children, and take ownership of the current gesture at any point.
此接口允许你监控事件分发给子对象的过程,并且可以在任何点上获取此事件的所有权。
Using this function takes some care, as it has a fairly complicated interaction with View.onTouchEvent(MotionEvent), and using it requires implementing that method as well as this one in the correct way. Events will be received in the following order:
用这个功能的时候需要小心一点儿,因为这个对View.onTouchEvent(MotionEvent)有适当的影响。并且用他的时候需要实View.onTouchEvent(MotionEvent),中的正确的方法。事件按照下面的顺序被获取:
1、You will receive the down event here
获取down事件
2、The down event will be handled either by a child of this view group, or given to your own onTouchEvent() method to handle;
这个down事件有可能会被这个Group中的任何一个子对象处理掉,或者被自身的onTouchEvent() 方法处理掉。
this means you should implement onTouchEvent() to return true, so you will continue to see the rest of the gesture (instead of looking for a parent view to handle it)
这就意味着你必须实现onTouchEvent()方法并且返回true,只有这样你才能够看到其余的手势动作(而不是找一个父view来处理他)
Also, by returning true from onTouchEvent(), you will not receive any following events in onInterceptTouchEvent() and all touch processing must happen in onTouchEvent() like normal.
同时,如果 onTouchEvent()返回为true,你也不会接收到 onInterceptTouchEvent()中其余的事件并且所有的事件处理必须像onTouchEvent() 一样正常处理。
3、For as long as you return false from this function, each following event (up to and including the final up) will be delivered first here and then to the target‘s onTouchEvent().
随着你返回了false,那么接下来的事件(up事件)会被先传到这个接口里,然后传给它的 onTouchEvent().
4、If you return true from here, you will not receive any following events: the target view will receive the same event but with the action ACTION_CANCEL, and all further events will be delivered to your onTouchEvent() method and no longer appear here.
此时如果你返回了true,你就不会再接收到其余的事件,目标view也会接收除了ACTION_CANCEL外也和它一样的事件。其余的事件会被传递到onTouchEvent() 方法中,并不会出现在此接口中。
事件关系
以上两个事件接口的解释内容是从api中翻译下来的。接下来就看ViewGroup和View的事件传递关系
1、ViewGroup和View都有onTouchuEvent事件
2、ViewGroup有onInterceptTouchEvent事件。
通过上面概念的分析我们知道了,onInterceptTouchEvent使用来控制事件传递的,是从父对象向子对象传递。onTouchuEvent是进行事件处理,它是由子对象向父对象进行冒泡,也就是说如果子对象处理了该事件(返回true),那么就不在向父对象传递了,父对象的onTouchEvent就收到down事件(以及以后的事件)了,
如果子对象处理不了该事件(返回false)那么该事件就会冒泡给父对象的onTouchEvent,如果此时父对象处理了(返回true),那么子对象也就无法处理以后的事件了;由onInterceptTouchEvent来决定是否对该事件进行拦截,如果事件在此处被拦截了,那么onTouchEvent就没有机会再去处理了。
比如,有以下的布局,并且在view上进行了一次点击。
<cn.bitlove.test.CLayout android:id="@+id/layout" > <cn.bitlove.test.CView android:id="@+id/view" /> </cn.bitlove.test.CLayout>
那么down事件首先被Layout的onInterceptTouchEvent事件接收,如果拦截(返回true)了,那么子对象的onTouchuEvent不会收到任何接下来的事件了,但是Layout自身的onTouchEvent依然可以处理;
如果未拦截,那么子对象的onTouchEvent就可以处理了,如果子对象的onTouchEvent函数处理的事件(返回true),那么父对象的onTouchEvent就没有机会再处理了。
如果子对象的onTouchEvent对象没有处理该事件(返回false),那么将交由父对象进行处理,之后的事件子对象无法控制。之后如果父对象处理了down事件,那么由父对象继续处理后续事件,如果父对象也没有处理这个down事件,那么继续向他的父对象继续冒泡该事件,如果都没有处理,那么整个事件链结束。
总结
可以这么说,touch事件事件链(donw->move->up),他由两个过程组成,一是事件的分发,二是事件的处理。先处理事件的分发,这个分发主要影响子对象,如果事件的分发被打断了,子对象自然没有后续的处理了。
事件的处理,是由子对象向父对象冒泡,谁从哪个事件点接手处理,谁就接手之后的整个事件链条,当然他也可以处理一些环节后,继续把后续的事件链交由父对象处理。需要注意的是,一旦事件链交出去后就没有机会再处理了,比如子对象处理完down事件后不想处理move事件,把move事件交由父对象处理, 等父对象处理完再由自己继续处理up事件,这样是不行的。事件链控制权总是向父对象冒泡,不能往回。
后记
把握住事件的两个链条,那么对事件的理解就清晰很多了