Android开发中的事件分发和消费机制的一些理解
分类:
文章
•
2022-10-31 14:36:43
-
说明
事件分发被用作解决事件冲突,还被用作自定义View
-
事件包含的动作
1.ACTION_DOWN(按下):手指只要一触摸屏幕就立即触发这个动作
2.ACTION_MOVE(移动):手指触摸屏幕,并在屏幕上移动一段距离。
3.ACTION_UP(抬起):手指离开屏幕的一瞬间就立即触发该动作。
-
三个动作,伴随三个方法
事件分发(dispatchTouchEvent),事件拦截(onInterceptTouchEvent),事件消费(onTouchEvent)
事件分发:将事件分发给拦截方法或者消费方法来处理。
事件拦截:该方法将事件拦截下来。不会直接传给下一级元素。
事件消费:最终这个事件被消费掉了。
-
Android提供可了三个控件来调用者三个事件方法的
Activity ViewGroup View(终极控件)
dispatchTouchEvent true true true
onInterceptTouchEvent false true false
onTouchEvent true true true
1.activity为什么没有拦截方法?
如果Activity有拦截,那么它将可以是一个没有界面的Service,那么将跟Service冲突。
2.View为什么没有拦截方法?
因为View属于终结控件,View没有子控件,也不能添加子控件,拦截没有意义。dispatchTouchEvent:它是一个分发方法,它拿到这个事件之后,只能调用onIntercepteTouchEvent或者onTouchEvent来传递这个事件,一般不去考虑这个返回值onIntercepteTouchEvent:它是一个拦截方法,它拿到dispatchTouchEvent传来的事件之后,判断自身是否要拦截,如果拦截该事件【也就是该方法的返回值为true】,那么它将直接调用当前控件自身的onTouchEvent来处理。如果不拦截该事件【采用默认的返回值或者返回值设置为false】,那么它将直接调用子控件的dispatchTouchEvent来分发。onTouchEvnet:它是一个消费方法,默认值是不消费的。不消费那么它将直接将该事件交于父控件或者组件来处理。
-
一个单击或者移动的动作的事件处理过程
1.单击 按下是谁消费了,抬起就一定得让谁消费。前后是同一个对象
2.移动 按下是前提,按下谁消费,抬起就谁消费。前后是同一个对象
总结:按下是谁消费,移动和抬起的动作就是谁来消费。
-
单击事件分发响应验证

单机 = 按下 + 抬起
A:Activity VG:ViewGroup V:View
1.采用默认值的方式,在界面中执行单机,方法调用过程是?
A:dispatch -> VG:dispatch -> VG:onIntercept -> V:dispatch -> V:onTouch -> VG:onTouch -> A:onTouch【按下事件消费结束】-> A:dispatch -> A:onTouch【抬起事件消费结束,单机事件完成】
2.让ViewGroup拦截该事件,也就是拦截方法返回true,其他方法默认。
A:dispatch -> VG:dispatch -> VG:onIntercept -> VG:onTouch -> A:onTouch【按下动作消费结束】-> A:dispatch -> A:onTouch【抬起事件消费结束,单机事件完成】
3.让View消费该事件,也就是view的onTouchEvent返回true,其他方法默认。
A:dispatch -> VG:dispatch -> VG:onIntercep -> V:dispatch -> V:onTouch【按下动作消费结束】-> A:dispatch -> VG:dispatch -> VG:onIntercept -> V:dispatch ->V:onTouch【抬起事件消费结束,单机事件完成】
4.让ViewGroup消费该事件,就是ViewGroup的onTouchEvent返回true,其他方法默认
A:dispatch -> VG:dispatch -> VG:onIntercept -> V:dispatch -> V:onTouch -> VG:onTouch【按下事件消费结束】-> A:dispatch -> VG:dispatch -> VG:onTouch【抬起事件消费结束,单机事件完成】
-
给控件设置长按监听或者单击监听事件
1.只要给控件**Clickable属性或者LongClickable属性,那么控件就可以消费按下动作。
1.直接在布局中给定这个clickable属性或者longclickable这个属性,并将它设置为true
2.可以直接给控件设置对应的监听方法。(OnClickListener,OnLongClickListener)
2.给View设置单击监听事件,其他方法返回值默认
A:dispatch -> VG:dispatch -> VG:onIntercept -> V:dispatch -> V:onTouch【**了Cliable属性】【按下事件消费结束】-> A:dispatch -> VG:dispatch ->VG:onIntercept -> V:dispatch -> V:onTouch【抬起事件消费结束】-> V:onClick【单机事件结束】
3.给View设置长按监听事件,其他方法返回值默认
A:dispatch -> VG:dispatch -> VG:onIntercept -> V:dispatch -> V:onTouch【**了Cliable属性】【按下事件消费结束】【->OnLongClick(必须要达到长按的时间标准,方可执行)】-> A:dispatch -> VG:dispatch -> VG:onIntercept -> V:dispatch -> V:onTouch【抬起事件消费结束】
4.给View设置单击事件和长按事件监听,方法的返回值默认
1.LongClick这个方法的返回值为false,默认值
A:dispatch -> VG:dispatch -> VG:onIntercept -> V:dispatch -> V:onTouch【**了Cliable属性】【按下事件消费结束】【->OnLongClick(必须要达到长按的时间标准,方可执行)】-> A:dispatch -> VG:dispatch -> VG:onIntercept -> V:dispatch -> V:onTouch【抬起事件消费结束】-> V:onClick【单机事件结束】
2.LongClick这个方法的返回值设置为true
onClick方法和onLongClick方法有且只有一个方法被执行,另外一个方法不执行。
结论:给控件设置单击和长按监听,会**控件的Clickable属性和onLongClick属性,只要**这任意一个属性,那么该控件就会消费该事件,就不会想上层传了。
-
【补充】
dispatchTouchEvent 方法,在这个方法内,首先是进行了一个判断,里面有三个条件,如果这三个条件都满足,就返 回true,否则就返回onTouchEvent方法执行的结果。对于第一个条件是一个mOnTouchListener变量,这个变量是在View中的setOnTouchListener方法里赋值的,也就是说只要我们给控件注册了touch事件,mOnTouchListener就一定被赋值了。第二个条件是判 断当前点击的控件是否是enable的,按钮默认都是enable的,因此这个条件恒定为true。第三个条件最为关键,
mOnTouchListener.onTouch(this, event),其实也就是去回调控件注册touch事件时的onTouch方法。也就是说如果我们在onTouch方法里返回true,就会让这三个条件全部成立,从而整个方法直接返回true。如果我们在onTouch方法里返回false,就会再去执行onTouchEvent(event)方法。结合我之前讲的例子,首先在 dispatchTouchEvent中最先执行的就是onTouch方法,因此onTouch肯定是要优先于onClick执行的,而如果在onTouch方法里返回了true,就会让
dispatchTouchEvent 方法直接返回true,不会再继续往下执行。