事件拦截机制分析(Android群英传)

内容是博主照着书敲出来的,博主码字挺辛苦的,转载请注明出处,后序内容陆续会码出。

  当Android系统捕获到用户的各种输入事件后,如何准确地传递给真正需要这个事件的控件呢?Android给我们提供了一整套完善的事件传递、处理机制,来帮助开发者完成准确的事件分配与处理。

  要了解触摸事件的拦截机制,首先要了解什么是触摸事件?顾名思义,触摸事件就是捕获触摸屏幕后产生的事件。当点击一个按钮时,通常就会产生两个或者三个事件——按钮按下,这是事件一;如果不小心滑动一点,这就是事件二;当手抬起,这是事件三。Android为触摸事件封装了一个类——MotionEvent,如果重写onTouchEvent()方法,那就会发现给方法的参数就是这样一个MotionEvent。其实,只要是重写触摸相关的方法,参数一般都含有MotionEvent,可见它的重要性。

  在MotionEvent里面封装了不少好东西,比如触摸点的坐标,可以通过event.getX()方法和event.getRawX()方法取出坐标点;再比如获得点击的事件类型,可以通过不同的Action(如MotionEvent.ACTION_DOWN、MotionEvent.ACTION_MOVE)来进行区分,并实现不同的逻辑。

  如此看来,触摸事件还是比较简单的,其实就是一个动作类型加坐标而已。但是我们知道,Android的View结构是树形结构,也就是说,View可以放在ViewGroup里面,通过不同的组合来实现不同的样式。那么问题来了,View放在一个ViewGroup里面,这个ViewGroup又放在另一个ViewGroup里面,甚至还有可能继续嵌套,一层层地叠起来。可我们的触摸事件就一个,到底该分给谁呢?同一个事件,子View和父ViewGroup都有可能想要进行处理。因此,这就产生了“事件拦截”这个“霸气”的称呼。

  当然,事件拦截可以很复杂,也可以很简单。但是初学者却经常“卡”在这里不知道如何继续进行,所以我们不想通过过多的源代码让大家不知所措。我们通过最直观的Log信息,让大家先有一个大概的了解,知道事件拦截的本质,然后大家在结合源代码学习时,就可以有方向、有目的性去理解了。

  首先,请想象一下生活中非常常见的场景:假设你所在的公司,有一个总经理,级别最高;他下面有一个部长,级别次之;最低层,就是干活的你,没有级别。现在董事会交给总经理一项任务,总经理将这项任务布置给了部长,部长又把任务安排给了你。而当你好不容易干完活了,你就把任务交给部长,部长觉得任务完成得不错,于是就签了他的名字交给总经理,总经理看了也觉得不错,就业签了名字交给董事会。这样,一个任务就顺利完成了。如果大家能非常清楚地理解这样一个场景,那么对于事件拦截机制,你就超过了40%的开发者了。下面,我们再来超越剩下的开发者。为了能过方便地了解整个事件的流程,我们设计了这样一个实例,如下图所示。

  一个总经理——MyViewGroupA,最外层的ViewGroup(红色)。

  一个部长——MyViewGroupB,中间的ViewGroup(绿色)。

  一个干活的你——MyView,在最底层(蓝色)。

  本实例的整个布局结构如下图所示。

  代码非常简单,只是重写了事件拦截和处理的几个方法,并给它加上了一些Log而已。

  对于ViewGroup来说,重写了如下所示的三个方法。

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    Log.d("blankj", "ViewGroupA dispatchTouchEvent" + ev.getAction());
    return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
    Log.d("blankj", "ViewGroupA onInterceptTouchEvent" + ev.getAction());
    return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
    Log.d("blankj", "ViewGroupA onTouchEvent" + event.getAction());
    return super.onTouchEvent(event);
}

  而对于View来说,重写了如下所示的两个方法。

@Override
public boolean onTouchEvent(MotionEvent event) {
    Log.d("blankj", "View onTouchEvent" + event.getAction());
    return super.onTouchEvent(event);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
    Log.d("blankj", "View dispatchTouchEvent" + event.getAction());
    return super.dispatchTouchEvent(event);
}

  从上面的代码中可以看到,ViewGroup级别比较高,比View多了一个方法——onInterceptTouchEvent()。这个方法看名字就能猜到是事件拦截的核心方法。我们先不修改任何返回值,只是点击一下View,然后看Log会怎样记录我们的操作和程序响应。点击View后的Log如下所示。

D/blankj: ViewGroupA dispatchTouchEvent0

D/blankj: ViewGroupA onInterceptTouchEvent0

D/blankj: ViewGroupB dispatchTouchEvent0

D/blankj: ViewGroupB onInterceptTouchEvent0

D/blankj: View dispatchTouchEvent0

D/blankj: View onTouchEvent0

D/blankj: ViewGroupB onTouchEvent0

D/blankj: ViewGroupA onTouchEvent0

  可以看见,正常情况下,时间的传递顺序是:

  总经理(MyViewGroupA)→部长(MyViewGroupB)→你(View)。事件传递的时候,先执行dispatchTouchEvent()方法,再执行onInterceptTouchEvent()方法。

  事件的处理顺序是:

  你(View)→部长(MyViewGroupB)→总经理(MyViewGroupA)。事件处理都是执行onTouchEvent()方法。

  事件传递的返回值非常容易理解:true,拦截,不继续;false,不拦截,继续流程。

  事件处理的返回值也类似:true,处理了,不用审核了;false,给上级处理。

  初始情况下,返回值都是false。

  这里为了能够方便大家理解事件拦截的过程,在事件传递中,我们只关心onInterceptTouchEvent(),而dispatchTouchEvent()方法虽然是事件分发的第一步,但一般情况下,我们不太会去改写这个方法,所以暂时不管这个方法。可以把上面的整个事件过程整理成如下图所示的一张图。

  相信大家只要把MyView想成自己,就能充分理解事件分发、拦截、处理的整个流程了。

  下面我们稍微改动一下,假设总经理(MyViewGroupA)发现这个任务太简单了,觉得自己完成就可以了,完全没必要再找下属。因此时间就被总经理(MyViewGroupA)使用onInterceptTouchEvent()方法把事件给拦截了,即让MyViewGroupA的onInterceptTouchEvent()方法返回true,我们再来看一下Log。

D/blankj: ViewGroupA dispatchTouchEvent0

D/blankj: ViewGroupA onInterceptTouchEvent0

D/blankj: ViewGroupA onTouchEvent0

  跟我们设想的一样,总经理(MyViewGroupA)把所有事情都干了,没后面人的事了。同理,我们让部长(MyViewGroupB)也来当一次好人,即让部长(MyViewGroupB)使用onInterceptTouchEvent()方法返回true,把事件拦截下来,Log就会使以下这样。

D/blankj: ViewGroupA dispatchTouchEvent0

D/blankj: ViewGroupA onInterceptTouchEvent0

D/blankj: ViewGroupB dispatchTouchEvent0

D/blankj: ViewGroupB onInterceptTouchEvent0

D/blankj: ViewGroupB onTouchEvent0

D/blankj: ViewGroupA onTouchEvent0

  可以看到,这次部长(MyViewGroupB)当了好人,你(MyView)就不用干活了。

  那么这两种情况,也可以整理成类似如上图所示的图。

  总经理(MyViewGroupA)拦截事件,如下图所示。

  部长(MyViewGroupB)拦截事件,如下图所示。

  对事件的分发、拦截,现在大家应该比较清楚了,下面我们再看看事件的处理。先来看看底层人民——你(MyView)。最开始的时候讲了,当你处理完任务后会向上级报告,需要上级的确认,所以你的事件处理返回false。那么你突然有一天受不了老板的压迫了,罢工不干了,那么你的任务就没人做了,也就不用报告上机了,所以就直接返回true。现在再来看看Log,如下所示。

D/blankj: ViewGroupA dispatchTouchEvent0

D/blankj: ViewGroupA onInterceptTouchEvent0

D/blankj: ViewGroupB dispatchTouchEvent0

D/blankj: ViewGroupB onInterceptTouchEvent0

D/blankj: View dispatchTouchEvent0

D/blankj: View onTouchEvent0

  可以看见,事件传递跟以前一样,但是事件处理,到你(MyView)这就结束了,因为你返回true,表示不用向上级汇报了。这时,我们同样来整理下关系图,如下所示。

  你(MyView)终于翻身做了主,决定了自己的命运。但是,如果部长(MyViewGroupB)看到了你的报告,觉得太丢人,不敢给经理看,所以他就偷偷地返回true,整个事件也就到此为止了,即部长(MyViewGroupB)将自己的onTouchEvent返回true,Log如下所示。

D/blankj: ViewGroupA dispatchTouchEvent0

D/blankj: ViewGroupA onInterceptTouchEvent0

D/blankj: ViewGroupB dispatchTouchEvent0

D/blankj: ViewGroupB onInterceptTouchEvent0

D/blankj: View dispatchTouchEvent0

D/blankj: View onTouchEvent0

D/blankj: ViewGroupB onTouchEvent0

  他们之间的关系图如下图所示。

  通过对前面几种情况的分析,相信大家能比较容易地了解事件的分发、拦截、处理事件的流程了。在后面的学习中,结合源码,你会更加深入地理解,为什么流程会是这样的?初学者在学习的时候,最好先对流程有一个大致的认识之后,再去接触源码,这样就不会一头雾水,摸不着头脑,从而丧失学习的兴趣。

项目地址→EventIntercept



原文地址事件拦截机制分析(Android群英传)

我的自媒体博客blankj小站(OJ、LeetCode、Android开发),欢迎来逛逛。

时间: 2024-10-10 13:26:03

事件拦截机制分析(Android群英传)的相关文章

Android群英传笔记——摘要,概述,新的出发点,温故而知新,能够为师矣!

Android群英传笔记--摘要.概述,新的出发点,温故而知新.能够为师矣! 当工作的越久,就越感到力不从心了,基础和理解才是最重要的,所以买了两本书,医生的<Android群英传>和主席的<Android开发艺术探索>.主要是再全面点的把自己所学的知识消化,这样也就不枉自己天天熬夜学习了,如今群英传快看完了.准备又一次再看一遍,同一时候把笔记以博客的形式记录下来,这样或许更加的深刻,然后再消磨一下主席的那本书,这本书有视频解说,更加好,所以估计以后应该非常少再写其它的博客,只是工

Android群英传笔记——第七章:Android动画机制和使用技巧

Android群英传笔记--第七章:Android动画机制和使用技巧 想来,最近忙的不可开交,都把看书给冷落了,还有好几本没有看完呢,速度得加快了 今天看了第七章,Android动画效果一直是人家中十分重要的一部分,从早期的Android版本中,由于动画机制和绘图机制的不健全,Android的人机交互备受诟病,Android从4.X开始,特别是5.X,动画越来越完善了,Google也开始重视这一方面了,我们本章学习的主要内容有 Android视图动画' Android属性动画 Android动画

Android群英传笔记——第九章:Android系统信息和安全机制

Android群英传笔记--第九章:Android系统信息和安全机制 本书也正式的进入尾声了,在android的世界了,不同的软件,硬件信息就像一个国家的经济水平,军事水平,不同的配置参数,代表着一个android帝国的强弱,所以厂商喜欢打配置战,本节就要是讲 Android系统信息的获取 PackageManager的使用 ActivityManager的使用 Android安全机制 一. Android系统信息的获取 由于android手机的开源性,手机的配置各种各样,那些优化大师之类的东西

Android群英传笔记——第八章:Activity与Activity调用栈分析

Android群英传笔记--第八章:Activity与Activity调用栈分析 开篇,我们陈述一下Activity,Activity是整个应用用户交互的核心组件,了解Activity的工作模式,生命周期和管理方式,是了解Android的基础,本节主讲 Activity的生命周期与工作模式 Activity调用栈管理 一.Activity Activity作为四大组建出现平率最高的组件,我们在哪里都能看到他,就让我们一起先来了解一下他的生命周期 1.起源 Activity是用户交互的第一接口,他

Android开发系列之事件拦截机制

对于Android开发者来说理解事件传递机制的重要性,我想应该是不言而喻的.在一个Activity里面,我们经常会重写onTouchEvent事件,可是重写结束之后,对于是返回true还是返回false却感到迷惑.心情好的时候返回true,心情不好的时候返回false. 要完全理解事件拦截机制,我们首先需要明白这几个方法的作用:dispatchTouchEvent(MotionEvent ev),onInterceptTouchEvent(MotionEvent ev),onTouchEvent

Android群英传知识点回顾——第八章:Activity与Activity调用栈分析

8.1 Activity 8.1.1 起源 8.1.2 Activity形态 8.1.3 生命周期 8.2 Activity任务栈简介 8.3 ActivityManifest启动模式 8.3.1 standard 8.3.2 singleTop 8.3.3 singleTask 8.3.4 singleInstance 8.4 Intent Flag启动模式 8.5 清空任务栈 8.6 Activity任务栈使用 四大组件中出现频率最高的组件 Activity是与用户交互的第一接口,它提供了一

ListView常用优化技巧(Android群英传)

内容是博主照着书敲出来的,博主码字挺辛苦的,转载请注明出处,后序内容陆续会码出. 前言:ListView--列表,它作为一个非常重要的显示方式,不管是在Web中还是移动平台中,都是一个非常好的.不开或缺的展示信息的工具.在Android中,ListView控件接管了这一重担,在大量的场合下,我们都需要使用这个控件.虽然在Android 5.X时代,RecyclerView在很多地方都在逐渐取代ListView,但ListView的使用范围依然非常的广泛,它这万年老大哥的地位也不是轻易就能撼动的.

Android群英传笔记——第十二章:Android5.X 新特性详解,Material Design UI的新体验

Android群英传笔记--第十二章:Android5.X 新特性详解,Material Design UI的新体验 第十一章为什么不写,因为我很早之前就已经写过了,有需要的可以去看 Android高效率编码-第三方SDK详解系列(二)--Bmob后端云开发,实现登录注册,更改资料,修改密码,邮箱验证,上传,下载,推送消息,缩略图加载等功能 这一章很多,但是很有趣,也是这书的最后一章知识点了,我现在还在考虑要不要写这个拼图和2048的案例,在此之前,我们先来玩玩Android5.X的新特性吧!

Android群英传笔记——第四章:ListView使用技巧

Android群英传笔记--第四章:ListView使用技巧 近期也是比較迷茫.可是有一点点还是要坚持的,就是学习了.近期离职了,今天也是继续温习第四章ListView,也拖了事实上也挺久的了,listview可谓是老牌大将了,非常多的应用场景都要使用它,他也是我们用得最多的控件之中的一个了,尽管如今出来了一个RecyclerView,可是ListView的地位一时半会儿还是撼动不了的.这就促使我们更加应该去把他掌握了 一.Listview经常使用优化技巧 我们一步步来把ListView学习好