安卓事件分发机制简解

版权声明:本文为 宇不语 原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_35064774/article/details/52865060

如有转载,请申明:
转载至 http://blog.csdn.net/qq_35064774/article/details/52865060

1. 为什么需要了解安卓事件机制

在开发过程中,我们免不了用到组合控件,这时候事件分发机制就显示比较重要了。
如果你不了解,就可能出现父容器无法响应事件、子控件事件和父控件有冲突等问题。
在了解分发机制之前,你需要知道安卓控件分类。

2. 安卓控件分类

Activity

Activity是一个应用程序组件,提供一个屏幕,用户可以用来交互为了完成某项任务。

ViewGroup

容器控件,可包含其他容器控件或非容器控件。
例如:LinearLayout

View

非容器组件
例如: TextView、Button等

3. 事件方法

方法名 描述 Activity ViewGroup View
dispatchTouchEvent 分发
onInterceptTouchEvent 拦截
onTouchEvent 响应


Activity和View没有onInterceptTouchEvent方法。
一种解释是Activity是事件的起点,没有拦截的必要;View是事件的终点,要么消费事件,要么往回传,也没有拦截的意义。


4. 事件执行顺序

默认情况

无事件消费和拦截的情况下:(也就是事件方法返回值都是默认值,未经覆盖修改)

Activity: dispatchTouchEvent
ViewGroup: dispatchTouchEvent
ViewGroup: onInterceptTouchEvent
View: dispatchTouchEvent
View: onTouchEvent
ViewGroup: onTouchEvent
Activity: onTouchEvent


Activity.dispatchTouchEvent中拦截

在Activity中覆盖dispatchTouchEvent方法
return true时

Activity: dispatchTouchEvent

return false时

Activity: dispatchTouchEvent


ViewGroup.dispatchTouchEvent中拦截

在ViewGroup中覆盖dispatchTouchEvent方法
return true时

Activity: dispatchTouchEvent
ViewGroup: dispatchTouchEvent

return false时

Activity: dispatchTouchEvent
ViewGroup: dispatchTouchEvent
Activity: onTouchEvent

由此便可以发现dispatchTouchEvent方法返回true和false的区别。
返回true表示自己消费了事件,事件就此消失,既不会分发给下级,也不回传给父级。
返回false表示拒绝分发给下级,但会回传给父级,父级调用onTouchEvent来处理事件。
而返回super.dispatchTouchEvent(event)时,表示先向下分发,然后在回传给父级。

ViewGroup.onInterceptTouchEvent中拦截

在ViewGroup中覆盖onInterceptTouchEvent方法
return true时

Activity: dispatchTouchEvent
ViewGroup: dispatchTouchEvent
ViewGroup: onInterceptTouchEvent
ViewGroup: onTouchEvent
Activity: onTouchEvent

return false时

Activity: dispatchTouchEvent
ViewGroup: dispatchTouchEvent
ViewGroup: onInterceptTouchEvent
View: dispatchTouchEvent
View: onTouchEvent
ViewGroup: onTouchEvent
Activity: onTouchEvent

返回true时,表示拦截下这个事件,所以向下传递的过程提前结束,就进入到回传阶段,这个阶段就是onTouchEvent的调用。
返回false时,和不拦截的情况一样,也就是说super.onInterceptTouchEvent(event)返回的也应该是false。

View.dispatchTouchEvent中拦截

在View中覆盖dispatchTouchEvent方法
return true时

Activity: dispatchTouchEvent
ViewGroup: dispatchTouchEvent
ViewGroup: onInterceptTouchEvent
View: dispatchTouchEvent

return false时

Activity: dispatchTouchEvent
ViewGroup: dispatchTouchEvent
ViewGroup: onInterceptTouchEvent
View: dispatchTouchEvent
ViewGroup: onTouchEvent
Activity: onTouchEvent

返回true时,和ViewGroup一样表示自己消费了事件,事件就此消失,既不会分发给下级,也不回传给父级。
返回false时,表示拒绝分发给下级,开始回传。需要注意的是,回传时,直接从父级开始,跳过了本控件。

View.onTouchEvent中拦截

在View中覆盖onTouchEvent方法
return true时

Activity: dispatchTouchEvent
ViewGroup: dispatchTouchEvent
ViewGroup: onInterceptTouchEvent
View: dispatchTouchEvent
View: onTouchEvent

return false时

Activity: dispatchTouchEvent
ViewGroup: dispatchTouchEvent
ViewGroup: onInterceptTouchEvent
View: onTouchEvent
View: dispatchTouchEvent
ViewGroup: onTouchEvent
Activity: onTouchEvent

返回true时,和dispatchTouchEvent类似,表示自己消费了事件,事件就此消失。
返回false时,表示不处理事件,和返回super.onTouchEvent(event)是一样的效果。因为不处理该事件,该事件就继续回传。
而ViewGroup、Activity的onTouchEvent方法与这一样,这里就不浪费笔墨了。

5. 总结

如何理解执行顺序

  1. 用户触屏后,消息先传递给Activity。Activity通过dispatchTouchEvent方法的返回值决定是否把消息分发给子控件。如果返回true或false,就不往下传;返回super.dispatchTouchEvent(event),就往下传。
    (客户提出一个需求,需求先提交给项目经理。项目经理判断是否可行,如果觉得不可行,就不通知项目组长;如果可行,传消息给组长。)

  2. ViewGroup控件收到消息,然后通过dispatchTouchEvent方法决定是否要分发给子控件。如果返回true,自己消费事件,不往下传也不回传;如果返回false,不往下传,但回传;返回super.dispatchTouchEvent(event),继续往下传。
    (组长收到需求,判断需求是否可行。如果组长发现需求纯属扯淡,一怒之下把需求说明书销毁了,并没有向上级报告;如果组长发现需求中有难点无法实现,就把需求回馈给经理;如果组长发现需求可行,就通知项目组的程序员。)

  3. ViewGroup在往下传递事件前,需要通过onInterceptTouchEvent判断一下是否要拦截事件。如果返回true,拦下事件,不往下传,回传给父级;如果返回false或super.onInterceptTouchEvent(event),则不拦截事件,往下传。
    (组长在往下发通知前,需要先给组员分配任务,然后审核任务清单。如果组长发现组内人员不足或缺少某方面的人员,则要把该需求拦截下来,回馈给经理;如果审核通过,正式传递给组员)

  4. View收到事件后,通过dispatchTouchEvent方法决定是否要分发给下级。如果返回true,事件就此消失;如果返回false,不往下分发,直接回传给父级;返回super.dispatchTouchEvent(event),就继续往下传。
    (程序员收到需求后,要判断一下是否可以实现。如果程序员发现需求太过刁钻,一气之下闪人了;如果程序员发现需求做不了,反馈给组长;如果需求可以实行,就准备开发。)

  5. View通过onTouchEvent响应事件。如果返回true表示处理了该事件,事件就此消失;如果返回false或super.onTouchEvent(event)表示不处理事件,直接回传给父级。
    (程序员准备开工。如果需求全都实现了,需求就此结束;如果发现部分细节无法实现,反馈给组长,询问组长是否能解决。)

  6. ViewGroup和Acivity的onTouchEvent与View的类似,这里不继续啰嗦了。


三个事件方法总结

dispatchTouchEvent

用于向下分发事件。
如果返回true,不往下传也不回传,事件消失;
如果返回false,不往下传,但回传。注意这里直接回传给父级,不调用自身的onTouchEvent。
如果返回super.dispatchTouchEvent(event),先往下传,然后回传(类似于二叉树遍历)。

onInterceptTouchEvent

用于拦截事件。
如果返回true,拦截事件,不往下传,回传事件。这里会先调用自己的onTouchEvent。
如果返回false或super.onInterceptTouchEvent(event),则不拦截事件,往下传。

onTouchEvent

用于响应事件。
如果返回true,表示消费事件,事件消失。
如果返回fasle或super.onTouchEvent(event)表示不处理事件,回传父级。

谷歌为何这样设计事件分发机制

即便你已经理解了上述事件分发的顺序,时间一长,难免记忆模糊。
在最后为了加深读者映像,简单说一下个人对于谷歌的事件分发机制的见解。

  1. 用户: 我想我大概需要这样一个功能,屏幕上有一个列表,列表每一项中有一个按钮,按钮和列表项都可以点击。
    当满足某条件时,可以点击按钮;条件不满足时,按钮不可点击,点击按钮就相当于点击了列表。
    另外,滑动列表的时候,要防止按钮的误点击。
  2. 程序: 这样的话,用户点击某一处之后,要先让最底层的组件得到事件,这样就能实现”滑动列表的时候,要防止按钮的误点击”。
    但要实现点击按钮,按钮先响应事件,只有按钮不响应事件时,才轮到列表响应事件。这个…有了,责任链模式。
  3. 程序: 整理了一下思路,用户点击某处后,先把事件给最底层的组件,底层组件依次往子组件分发事件,在任何一个分发过程中,组件都有拒绝分发和拦截事件的权利,这样就可以实现父组件的事件过滤功能(具体点就是列表滑动过程中,手指滑倒按钮也不会触发按钮的事件);当事件传递到最顶层的子组件时,子组件判断是否需要响应事件,如果响应了,事件就此停止,否则传递给父组件(具体来说,就是你点击了按钮,只有按钮会响应事件,而如果按钮是不可点击的状态,事件就由列表响应)。



个人猜想设计过程应该是这样的,哈哈哈T v T

猜你喜欢

转载自blog.csdn.net/qq_35064774/article/details/52865060
今日推荐