【朝花夕拾】Android自定义View篇之(五)Android事件分发及传递机制

前言

在自定义View中,经常需要处理Android事件分发的问题,尤其在有多个输入设备(如遥控、鼠标、游戏手柄等)时,事件处理问题尤为突出。Android事件分发机制,一直以来都是一个让众多开发者困扰的难点,至少笔者在工作的前几年中,没有特意研究它之前,就经常云里雾里。实际上,该问题的“七寸”就是dispatchTouchEvent(MotionEvent ev)、onInterceptTouchEvent(MotionEvent ev)、onTouchEvent(MotionEvent ev)这三个方法和MotionEvent事件实体,咱们这里索性称它们为“四大恶人”吧。本文将主要通过示例演示的方式来打这个“七寸”吧。

一、事件分发机制与生活场景的类比

Android的事件分发机制和生活中的很多场景有着相似之处,可能Android的很多设计灵感就是来源于生活吧。

1、示例中的四个角色

在众多示例当中,有一个经常被拿来举例的经典场景就是PM(项目经理)、Team Leader、Programmer之间工作安排的问题,咱们这里也用这个场景来类比,另外再加一个Boss的角色,以便于理解。现在这个公司中四个角色职位从高到低依次为,Boss > PM > Team Leader > Programmer。

2、可能出现的场景

一般来说,一个寻常的工作流程是:Boss想到有个功能很流行,就会指示PM去办,PM会分发给Team Leader,而Team Leader会安排Proggrammer去编程实现。Boss是决策者,事件来源于他;PM和Team Leader是管理者,负责一层层把任务派发给自己的下属;Programmer是具体来做开发的,所以事件最后落在他的身上了,最后由他来完成。但是现实工作中,工作并不总是这个流程,还有很多其它场景,比如:

(1)市面上出现了一很火的行业,做智能手机。本公司是否需要也涉足这个行业,需要Boss自己开董事会来做决策。那么这个事情就是Boss应该处理的事情,他就不会派发给PM,Boss以下的员工看来,就跟没有任何事情一样。

(2)Boss确定了智能手机是一个很有前景的行业,确定了要做,于是就召集PM,做好立项工作。那这个立项工作就是这个PM的工作了,如何立项,需要招聘什么样人等各项准备工作,PM就得自己做好整个计划,事情就到他这里为止了,不会再往下派发。

(3)做手机挣了钱,Boss决定奖励一些表现优异的Programmer。这需要先安排PM,PM然后安排下面的Team Leader对下面的Programmer们做综合考量,将优秀者报上去。这就是Team Leader需要完成的工作,他也无法再传下去。

(4)如前面说到的,Boss要求做一个市面上很流行的功能,经过PM和Team Leader层层派发到了Programmer手上。虽然事情派发到了Programmer手上,但也有两种情形:

1)在Programmer能力范围内,做得很完美。这种情况事情就在这里被处理掉了,无需再传递出去了。后续Boss一系列的类似功能也会继续派发下来,也以这样的流程来走下去。

2)Programmer能力有限,做不了。这种情况下,他就需要告诉Team Leader这一情况,把这个任务再依次传递给自己的领导。这样又有两种情况:①Team Leader 或者PM本身也是研发出身,开发能力也很强,就把这个开发任务给完成了,这样事情也就到此为止不再传递了。在Boss看来,下面的团队有能力开发好这类需求,于是后续Boss一系列的类似功能也会继续派发下来,仍然以这样的流程来走下去,到能处理这个事情的责任人处理完为止。②Team Leader和PM都是管理出身,这个功能他们也不会做。于是就层层往上报,最后到老板那里,老板自己处理,是不了了之还是再找招人,或者自己也是个大牛程序员自己可以开发出来,这个决策由老板来做。老板知道自己下面的团队完成不了这一系列任务,后续一系列这类功能,就不再派发下去了。

......

上述列出了一些比较有代表性的可能情况,下面咱们根据这些情况,来理解事件的分发机制。其实这里PM和Team Leader可以整体作为一个角色来看,只是为了后面看代码和日志方便对应,才分开为两个角色的。

二、MotionEvent简介

在讲Android事件分发机制前,先简单了解一些MotionEvent,因为它就是这个“事件”。以下截取了部分源码中的描述:

 1 ......
 2  * <p>
 3  * Motion events describe movements in terms of an action code and a set of axis values.
 4  * The action code specifies the state change that occurred such as a pointer going
 5  * down or up.  The axis values describe the position and other movement properties.
 6  * </p>
 7 ......
 8 public final class MotionEvent extends InputEvent implements Parcelable {
 9     public static final int ACTION_DOWN  = 0;
10     public static final int ACTION_UP  = 1;
11     public static final int ACTION_MOVE  = 2;
12     ......
13 }

MotionEvent,顾名思义,动作事件的意思。它通过一个action码和一套坐标值来描述动作。action码指定了当如指针按下或者抬起等事件发生时的状态改变,坐标值则描述了事件在屏幕中的位置和其它动作属性值。如下内容为MotionEvent的toString方法打印出来的结果:

MotionEvent { action=ACTION_DOWN, actionButton=0, id[0]=0, x[0]=173.0, y[0]=138.0, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x2, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=34268429, downTime=34268429, deviceId=8, source=0x1002 }

从这里可以看到,事件发生的action,时间,坐标等很多信息。本文中暂时只关注aciton这个字段,“action=ACTION_DOWN”表示了按下事件。

平时触摸屏幕时,一个最简单的事件包括了“ACTION_DOWN”和“ACTION_UP”,“ACTION_DOWN”表示手指按下,而““ACTION_UP”表示手指抬起来,这两个action才构成了一个完整的事件。如果手指在屏幕上有移动,还会包含“ACTION_MOVE”,此时一个完整的事件就包括“ACTION_DOWN”,多个“ACTION_MOVE”,“ACTION_UP”。当然,实际工作中会有很多复杂的情况出现,可能会出现一些其它的aciton,本文为了演示的方便,只考虑“ACTION_DOWN”和“ACTION_UP”的场景。

三、代码示例及默认场景分析

为了演示事件分发机制的工作流程,这里编写一个示例来进行演示。整个Acitivity模拟Boss角色;在其界面中的最外层模拟PM,继承自RelativeLayout,是一个父布局;PM下嵌套一层,也是一个父布局,继承自RelativeLayout,用于模拟Team Leader;最里面一层是一个叶子View,继承自Button,模拟Programmer。效果图及对应代码分别如下。

1、演示界面

2、默认场景下的代码示例

如下的代码中,需要重写的方法dispatchTouchEvent(MotionEvent ev)、onInterceptTouchEvent(MotionEvent ev)、onTouchEvent(MotionEvent ev)均返回默认值,即super.xxx。平时咱们使用系统原生控件时,无法修改它们的源码,所以系统给的默认场景就是这样的。

(1)Boss:EventDemoActivity

 1 public class EventDemoActivity extends AppCompatActivity {
 2
 3     @Override
 4     protected void onCreate(Bundle savedInstanceState) {
 5         super.onCreate(savedInstanceState);
 6         setContentView(R.layout.activity_event_demo);
 7     }
 8
 9     @Override
10     public boolean dispatchTouchEvent(MotionEvent ev) {
11         Log.i("songzheweiwang","[EventDemoActivity-->dispatchTouchEvent]ev="+EventUtil.parseAction(ev.getAction()));
12         return super.dispatchTouchEvent(ev);
13     }
14
15     @Override
16     public boolean onTouchEvent(MotionEvent event) {
17         Log.i("songzheweiwang","[EventDemoActivity-->onTouchEvent]event="+EventUtil.parseAction(event.getAction()));
18         return super.onTouchEvent(event);
19     }
20 }

该Activity的布局文件为

 1 //==========================activity_event_demo.xml===========================
 2 <?xml version="1.0" encoding="utf-8"?>
 3 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 4     android:layout_width="match_parent"
 5     android:layout_height="match_parent">
 6
 7     <com.example.demos.customviewdemo.ViewGroupOuter
 8         android:layout_width="300dp"
 9         android:layout_height="300dp"
10         android:layout_centerInParent="true"
11         android:background="@android:color/holo_orange_dark">
12
13         <com.example.demos.customviewdemo.ViewGroupMiddle
14             android:layout_width="200dp"
15             android:layout_height="200dp"
16             android:layout_centerInParent="true"
17             android:background="@android:color/holo_blue_dark">
18
19             <com.example.demos.customviewdemo.ViewInner
20                 android:id="@+id/viewInner"
21                 android:layout_width="100dp"
22                 android:layout_height="100dp"
23                 android:layout_centerInParent="true"
24                 android:background="@android:color/holo_green_dark"/>
25         </com.example.demos.customviewdemo.ViewGroupMiddle>
26     </com.example.demos.customviewdemo.ViewGroupOuter>
27 </RelativeLayout>

(2)PM:ViewGroupOuter

 1 public class ViewGroupOuter extends RelativeLayout {
 2
 3     public ViewGroupOuter(Context context, @Nullable AttributeSet attrs) {
 4         super(context, attrs);
 5     }
 6
 7     @Override
 8     public boolean dispatchTouchEvent(MotionEvent ev) {
 9         Log.i("songzheweiwang","[ViewGroupOuter-->dispatchTouchEvent]ev="+EventUtil.parseAction(ev.getAction()));
10         return super.dispatchTouchEvent(ev);
11     }
12
13     @Override
14     public boolean onInterceptTouchEvent(MotionEvent ev) {
15         Log.i("songzheweiwang","[ViewGroupOuter-->onInterceptTouchEvent]ev="+EventUtil.parseAction(ev.getAction()));
16         return super.onInterceptTouchEvent(ev);
17     }
18
19     @Override
20     public boolean onTouchEvent(MotionEvent event) {
21         Log.i("songzheweiwang","[ViewGroupOuter-->onTouchEvent]event="+EventUtil.parseAction(event.getAction()));
22         return super.onTouchEvent(event);
23     }
24 }

(3)Team Leader:ViewGroupMiddle

 1 public class ViewGroupMiddle extends RelativeLayout {
 2
 3     public ViewGroupMiddle(Context context, @Nullable AttributeSet attrs) {
 4         super(context, attrs);
 5     }
 6
 7     @Override
 8     public boolean dispatchTouchEvent(MotionEvent ev) {
 9         Log.i("songzheweiwang","[ViewGroupMiddle-->dispatchTouchEvent]ev="+EventUtil.parseAction(ev.getAction()));
10         return super.dispatchTouchEvent(ev);
11     }
12
13     @Override
14     public boolean onInterceptTouchEvent(MotionEvent ev) {
15         Log.i("songzheweiwang","[ViewGroupMiddle-->onInterceptTouchEvent]ev="+EventUtil.parseAction(ev.getAction()));
16         return super.onInterceptTouchEvent(ev);
17     }
18
19     @Override
20     public boolean onTouchEvent(MotionEvent event) {
21         Log.i("songzheweiwang","[ViewGroupMiddle-->onTouchEvent]event="+EventUtil.parseAction(event.getAction()));
22         return super.onTouchEvent(event);
23     }
24 }

(4)Programmer:ViewInner

这里先以Button为例,因为Button默认是可以处理Touch事件的,也就是说,事件传到这里时,能被完美地处理掉。

 1 @SuppressLint("AppCompatCustomView")
 2 public class ViewInner extends Button{
 3
 4     public ViewInner(Context context, AttributeSet attrs) {
 5         super(context, attrs);
 6     }
 7
 8     @Override
 9     public boolean dispatchTouchEvent(MotionEvent event) {
10         Log.i("songzheweiwang","[ViewInner-->dispatchTouchEvent]event="+EventUtil.parseAction(event.getAction()));
11         return super.dispatchTouchEvent(event);
12     }
13
14     @Override
15     public boolean onTouchEvent(MotionEvent event) {
16         Log.i("songzheweiwang","[ViewInner-->onTouchEvent]event="+EventUtil.parseAction(event.getAction()));
17         return super.onTouchEvent(event);
18     }
19 }

(5)辅助类:

 1 public class EventUtil {
 2     public static String parseAction(int action) {
 3         String actionName = "Unknow:action=" + action;
 4         switch (action) {
 5             case MotionEvent.ACTION_DOWN:
 6                 actionName = "ACTION_DOWN";
 7                 break;
 8             case MotionEvent.ACTION_MOVE:
 9                 actionName = "ACTION_MOVE";
10                 break;
11             case MotionEvent.ACTION_UP:
12                 actionName = "ACTION_UP";
13                 break;
14            default:
15                break;
16         }
17         return actionName;
18     }
19 }

3、日志

点击上图中不同的区域,会有不同的结果。这里点击最中间的View,点击其他区域的结果及分析,我们在后面再介绍。

 1 06-07 13:35:23.483 18298-18298/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_DOWN
 2 06-07 13:35:23.483 18298-18298/com.example.demos I/songzheweiwang: [ViewGroupOuter-->dispatchTouchEvent]ev=ACTION_DOWN
 3 06-07 13:35:23.483 18298-18298/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onInterceptTouchEvent]ev=ACTION_DOWN
 4 06-07 13:35:23.483 18298-18298/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->dispatchTouchEvent]ev=ACTION_DOWN
 5 06-07 13:35:23.483 18298-18298/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onInterceptTouchEvent]ev=ACTION_DOWN
 6 06-07 13:35:23.483 18298-18298/com.example.demos I/songzheweiwang: [ViewInner-->dispatchTouchEvent]event=ACTION_DOWN
 7 06-07 13:35:23.483 18298-18298/com.example.demos I/songzheweiwang: [ViewInner-->onTouchEvent]event=ACTION_DOWN
 8 06-07 13:35:23.524 18298-18298/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_UP
 9 06-07 13:35:23.525 18298-18298/com.example.demos I/songzheweiwang: [ViewGroupOuter-->dispatchTouchEvent]ev=ACTION_UP
10 06-07 13:35:23.525 18298-18298/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onInterceptTouchEvent]ev=ACTION_UP
11 06-07 13:35:23.525 18298-18298/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->dispatchTouchEvent]ev=ACTION_UP
12 06-07 13:35:23.525 18298-18298/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onInterceptTouchEvent]ev=ACTION_UP
13 06-07 13:35:23.525 18298-18298/com.example.demos I/songzheweiwang: [ViewInner-->dispatchTouchEvent]event=ACTION_UP
14 06-07 13:35:23.525 18298-18298/com.example.demos I/songzheweiwang: [ViewInner-->onTouchEvent]event=ACTION_UP

4、结果分析

该事件包含了两action:ACTION_DOWN和ACTION_UP。前我们说过,Programmer完美处理好了事件,本次流程就到这里为止了,不再传递,Boss认为团队有能力处理这类任务,所以类似的任务也会同样会交给手下的团队,所以ACTION_UP也走了类似的流程,那么整个事件就算完成了,由Programmer完美完成。整个事件的序列图如下所示:

5、ViewInner没有能力处理的情况

上面的例子中,ViewInner是一个Button,它默认是有能力处理这次Touch事件的。但是如果这是一个默认没有能力处理该时间的控件,又会是一种怎样的情形呢?咱们把ViewInner改为继承View再看看结果(仍然点击中间的ViewInner)。

 1 06-07 15:04:25.815 19652-19652/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_DOWN
 2 06-07 15:04:25.815 19652-19652/com.example.demos I/songzheweiwang: [ViewGroupOuter-->dispatchTouchEvent]ev=ACTION_DOWN
 3 06-07 15:04:25.815 19652-19652/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onInterceptTouchEvent]ev=ACTION_DOWN
 4 06-07 15:04:25.815 19652-19652/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->dispatchTouchEvent]ev=ACTION_DOWN
 5 06-07 15:04:25.815 19652-19652/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onInterceptTouchEvent]ev=ACTION_DOWN
 6 06-07 15:04:25.815 19652-19652/com.example.demos I/songzheweiwang: [ViewInner-->dispatchTouchEvent]event=ACTION_DOWN
 7 06-07 15:04:25.815 19652-19652/com.example.demos I/songzheweiwang: [ViewInner-->onTouchEvent]event=ACTION_DOWN
 8 06-07 15:04:25.816 19652-19652/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onTouchEvent]event=ACTION_DOWN
 9 06-07 15:04:25.816 19652-19652/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onTouchEvent]event=ACTION_DOWN
10 06-07 15:04:25.816 19652-19652/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_DOWN11 06-07 15:04:25.865 19652-19652/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_UP
12 06-07 15:04:25.865 19652-19652/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_UP

此时,整个事件的流程就变成了现在这样,直观地表现为如下的流程图:

为什么是这种结果呢?因为ViewInner继承自View,其默认情况下,是没有处理Touch事件的能力的。所以Programmer处理不了了,就一层一层网上报告,由于ViewGroupMiddle和ViewGroupOuter都继承自RelativeLayout,默认也是没有处理Touch事件的能力的,所以最后ACTION_DOWN事件就回到了Boss这里,由Boss自己来处理。Boss发现自己首先的团队无法处理这类事件,所以后面的ACTION_UP事件就自己处理了,而没有再往下派发了。这一点,和第二节中的第(4)点的第2)小点的情况②的场景是一致的。

如果在activity_event_demo.xml中为各个控件(包括父布局)加上属性[android:clickable="true"],或者在Activity中为对应控件添加监听点击事件,那么这个控件就有了处理Touch事件的能力了,就和之前使用Button的场景一样的。读者还可以试试在ViewInnner为View时,其父布局有处理Touch事件的能力时的场景(注意,要点击ViewInner来测试),那么这就是和第二节中的第(4)点的第2)小点的情况①的场景是一致的,这里咱们不再分析日志画流程图了。

四、Touch事件主要方法说明

前面一直提到Touch事件的3个主要方法:dispatchTouchEvent(MotionEvent ev)、onInterceptTouchEvent(MotionEvent ev)和onTouchEvent(MotionEvent ev),那么这三个方法的功能究竟是说明呢?在哪里可以用,在哪里不能用呢?这里可以看看下面的表格:

其实我们可用从函数名称来大致判断其功能,dispatchTouchEvent,分发触摸事件,就是把事件传递下去,准确来说就是是否要传递到子View以及自己的onInterceptTouchEvent方法和onTouchEvent方法,也就是说,不仅管子Viiew,还管自身剩下的俩个回调方法。onInterceptTouchEvent,事件拦截,它只管自身子View,而不会影响到自身后面两个方法的执行,如果拦截了,可以记忆为让自己的手下们无事可做。这两个方法容易混淆,需要重点理解和记忆。

在上述表格中还可以看到,Activity是无法回调onIntercepTouchEvent方法的,因为这个方法是ViewGroup中的方法,而Activity也不是View体系中,不是视图类,所以没有这个方法。我们可以这样记忆,Activity是Boss,不是打工行列中的一员,自己的任务就是让下面的打工者没去做事情,所有该方法对他来说,没有意义。叶子View也没有这个方法,因为自己没有子View了,也没有拦截的意义。

由于这三个方法都是boolean值,再加上默认情形下会返回super.xxx,这样,每一个方法都会有三种可选值。咱们这里先了解一下没一种取值会产生怎么样的结果。

(1)事件分发:public boolean dispatchTouchEvent(MotionEvent ev)

Touch事件发生时,Activity的dispatchTouchEvent方法会将事件传递给最外层控件的dispatchTouchEvent方法,并由该控件进行分发下去。从根元素依次往下传递,一直到最里面的叶子View,或者中途被某个控件终止,才结束这个派发过程。其分发逻辑如下:

1)如果 return true,事件会分发到当前控件的dispatchTouchEvent方法中处理。同时事件停止往下派发,且当前控件的onInterceptTouchEvent和onTouchEvent都不会执行。(这里笔者也不清楚事件是被处理了,还是不了了之了。从打印的log上看,和被处理掉的情形很相似,本次事件,如ACTION_DOWN,到此为止,然后Activity继续给出后续事件,如ACTION_UP,继续走到当前控件的这个方法中,知道这一系列事件全部走完。但是从实验结果看,如果在acitivty中给该控件添加了点击事件,发现点击后没有响应,说明这个时间没有被处理。所以笔者很纳闷,暂时还没有找到权威的说法。)

2)如果 return false,事件停止往下派发,且当前控件的onInterceptTouchEvent和onTouchEvent也都不会执行。同时将事件返回给上一级的onTouchEvent事件,由上一级去决定处理还是继续往上传递,自己不处理。

3)返回默认的super.dispatchTouchEvent,事件会自动分发给当前View的onInterceptTouchEvent。

(2)事件拦截:public boolean onInterceptTouchEvent(MotionEvent ev)

在当前控件的dispatchTouchEvent方法返回默认的方式时,其拦截逻辑如下:

1)return true,表示将事件进行拦截,并将拦截到的事件交给当前控件的onTouchEvent来处理。

2)return false,表示将事件放行,事件会被传递到子View上,并由子View的dispatchTouchEvent方法继续派发。

3)return super.onInterceptTouchEvent(ev),和返回false的逻辑一样。

(3)事件响应:public boolean onTouchEvent(MotionEvent ev)

该事件会响应的情形有如下两种:1)子View没有处理事件,将事件返回来;2)当前控件中dispatchTouchEvent返回默认的super.dispatchTouchEvent的情况下,且该控件的onInterceptTouchEvent返回false或者默认的super.onInterceptTouchEvent时。onTouchEvent事件响应逻辑如下:

1)返回true,当前事件会被处理掉。

2)返回false,当前事件不会被处理,返回给上一级的onTouchEvent方法来处理。

3)返回super.onTouchEvent,如果自己有能力处理该事件,则会处理,super.onTouchEvent的值为true;否则,如果自己没有能力处理该事件,则将事件返回到上一级中的onTouchEvent方法中处理,当前super.onTouchEvent的值为false。

五、Touch的3个主要方法返回值对事件分发影响的案例分析

上一节中介绍了Touch的3个主要方法的返回值下,对事件分发的处理逻辑。本节中,咱们通过修改签名实例中的返回值,来看看事件的分发流程(注意:以下情况下均点击中间的ViewInner控件)。

1、ViewGroupMiddle中dispatchTouchEvent返回true,其它均返回默认值时。

1 06-07 19:15:53.220 25298-25298/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_DOWN
2 06-07 19:15:53.221 25298-25298/com.example.demos I/songzheweiwang: [ViewGroupOuter-->dispatchTouchEvent]ev=ACTION_DOWN
3 06-07 19:15:53.221 25298-25298/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onInterceptTouchEvent]ev=ACTION_DOWN
4 06-07 19:15:53.222 25298-25298/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->dispatchTouchEvent]ev=ACTION_DOWN6 06-07 19:15:53.237 25298-25298/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_UP
7 06-07 19:15:53.237 25298-25298/com.example.demos I/songzheweiwang: [ViewGroupOuter-->dispatchTouchEvent]ev=ACTION_UP
8 06-07 19:15:53.237 25298-25298/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onInterceptTouchEvent]ev=ACTION_UP
9 06-07 19:15:53.238 25298-25298/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->dispatchTouchEvent]ev=ACTION_UP

参照第四节中的结论,ViewGroupMiddle的dispatchTouchEvent返回true,事件从Acitivty,经过ViewGroupOuter分发到ViewGroupMiddle中,在其dispatchTouchEvent方法中处理。ViewGroupMiddle的onInterceptTouchEvent和onTouchEvent均不会被调用,且事件也不会再往ViewInner中传递。既然事件是在ViewGroupMiddle的dispatchTouchEvent中被处理了,在Boss  EventDemoActivity看来,自己手下的团队有能力处理这类事件,所以ACTION_UP也被派发下来,走同样的流程,直到所有事件处理完毕。

2、ViewGroupMiddle中dispatchTouchEvent返回false,其它均返回默认值时

1 06-07 19:31:50.093 25668-25668/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_DOWN
2 06-07 19:31:50.094 25668-25668/com.example.demos I/songzheweiwang: [ViewGroupOuter-->dispatchTouchEvent]ev=ACTION_DOWN
3 06-07 19:31:50.094 25668-25668/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onInterceptTouchEvent]ev=ACTION_DOWN
4 06-07 19:31:50.094 25668-25668/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->dispatchTouchEvent]ev=ACTION_DOWN
5 06-07 19:31:50.094 25668-25668/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onTouchEvent]event=ACTION_DOWN
6 06-07 19:31:50.094 25668-25668/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_DOWN
7 06-07 19:31:50.151 25668-25668/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_UP
8 06-07 19:31:50.151 25668-25668/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_UP

参照第四节中的结论,ViewGroupMiddle的dispatchTouchEvent返回true,事件从Acitivty,经过ViewGroupOuter分发到ViewGroupMiddle中,且在dispatchTouchEvent方法中不处理此事件。ViewGroupMiddle的onInterceptTouchEvent和onTouchEvent均不会被调用,且事件也不会再往ViewInner中传递。自己处理不了事件,传递给上一级的onTouchEvent来处理,上一级也没能力处理,最后传给了EventDemoActivity的onTouchEvent。此时,在Boss看来,自己手下团队处理不了这类事件,所以后面的事件就不再传递下去,都有自己来处理。

3、ViewGroupMiddle中onInterceptTouchEvent返回true,其它均返回默认值时

 1 06-07 19:41:08.894 26055-26055/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_DOWN
 2 06-07 19:41:08.894 26055-26055/com.example.demos I/songzheweiwang: [ViewGroupOuter-->dispatchTouchEvent]ev=ACTION_DOWN
 3 06-07 19:41:08.894 26055-26055/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onInterceptTouchEvent]ev=ACTION_DOWN
 4 06-07 19:41:08.894 26055-26055/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->dispatchTouchEvent]ev=ACTION_DOWN
 5 06-07 19:41:08.894 26055-26055/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onInterceptTouchEvent]ev=ACTION_DOWN
 6 06-07 19:41:08.894 26055-26055/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onTouchEvent]event=ACTION_DOWN
 7 06-07 19:41:08.894 26055-26055/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onTouchEvent]event=ACTION_DOWN
 8 06-07 19:41:08.895 26055-26055/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_DOWN
 9 06-07 19:41:08.900 26055-26055/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_UP
10 06-07 19:41:08.901 26055-26055/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_UP

事件在ViewGroupMiddle中被拦截了,事件不再派发到ViewInner中,而是交给自己的onTouchEvent来处理。前面说过,ViewGroupMiddle继承自RelativeLayout,默认是没有能力处理Touch事件的,于是就传递到上一级的onTouchEvent中,直到EventDemoActivity中的onTouchEvent方法。此时,在Boss看来,自己手下团队处理不了这类事件,所以后面的事件就不再传递下去,都有自己来处理。

4、ViewGroupMiddle中onInterceptTouchEvent返回false,其它均返回默认值时

 1 06-07 19:48:58.130 26400-26400/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_DOWN
 2 06-07 19:48:58.130 26400-26400/com.example.demos I/songzheweiwang: [ViewGroupOuter-->dispatchTouchEvent]ev=ACTION_DOWN
 3 06-07 19:48:58.130 26400-26400/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onInterceptTouchEvent]ev=ACTION_DOWN
 4 06-07 19:48:58.130 26400-26400/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->dispatchTouchEvent]ev=ACTION_DOWN
 5 06-07 19:48:58.130 26400-26400/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onInterceptTouchEvent]ev=ACTION_DOWN
 6 06-07 19:48:58.130 26400-26400/com.example.demos I/songzheweiwang: [ViewInner-->dispatchTouchEvent]event=ACTION_DOWN
 7 06-07 19:48:58.131 26400-26400/com.example.demos I/songzheweiwang: [ViewInner-->onTouchEvent]event=ACTION_DOWN
 8 06-07 19:48:58.131 26400-26400/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onTouchEvent]event=ACTION_DOWN
 9 06-07 19:48:58.131 26400-26400/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onTouchEvent]event=ACTION_DOWN
10 06-07 19:48:58.131 26400-26400/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_DOWN
11 06-07 19:48:58.162 26400-26400/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_UP
12 06-07 19:48:58.162 26400-26400/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_UP

这种情况下,和使用默认super.onInterceptTouchEvent时是一样的,Log中中的日志也验证了这一点。事件派发流程在第三节中详细讲解过,这里就不再赘述了。

5、ViewGroupMiddle中onTouchEvent为true,其它均返回默认值时

 1 06-07 19:53:51.516 26711-26711/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_DOWN
 2 06-07 19:53:51.517 26711-26711/com.example.demos I/songzheweiwang: [ViewGroupOuter-->dispatchTouchEvent]ev=ACTION_DOWN
 3 06-07 19:53:51.517 26711-26711/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onInterceptTouchEvent]ev=ACTION_DOWN
 4 06-07 19:53:51.517 26711-26711/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->dispatchTouchEvent]ev=ACTION_DOWN
 5 06-07 19:53:51.517 26711-26711/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onInterceptTouchEvent]ev=ACTION_DOWN
 6 06-07 19:53:51.517 26711-26711/com.example.demos I/songzheweiwang: [ViewInner-->dispatchTouchEvent]event=ACTION_DOWN
 7 06-07 19:53:51.517 26711-26711/com.example.demos I/songzheweiwang: [ViewInner-->onTouchEvent]event=ACTION_DOWN
 8 06-07 19:53:51.517 26711-26711/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onTouchEvent]event=ACTION_DOWN
 9 06-07 19:53:51.582 26711-26711/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_UP
10 06-07 19:53:51.583 26711-26711/com.example.demos I/songzheweiwang: [ViewGroupOuter-->dispatchTouchEvent]ev=ACTION_UP
11 06-07 19:53:51.583 26711-26711/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onInterceptTouchEvent]ev=ACTION_UP
12 06-07 19:53:51.583 26711-26711/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->dispatchTouchEvent]ev=ACTION_UP
13 06-07 19:53:51.583 26711-26711/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onTouchEvent]event=ACTION_UP

事件依次传递到ViewInner的onTouchEvent方法中,ViewInner默认没有能力处理该事件,传递到上一级ViewGroupMiddle中的onTouchEvent来处理。返回true表示被处理了,本次事件在此中止了。在Boss看来,手下团队有能力处理这类事件,且在ViewGroupMiddle的onTouchEvent中完成,说明ViewInner没有能力处理,所以下一个事件ACTION_UP时,ViewGroupMiddle就没有把事件派发到ViewInner中,而是直接给自己的onTouchEvent方法中来处理。

5、ViewGroupMiddle中onTouchEvent为false,其它均返回默认值时

 1 06-07 20:09:49.746 27357-27357/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_DOWN
 2 06-07 20:09:49.746 27357-27357/com.example.demos I/songzheweiwang: [ViewGroupOuter-->dispatchTouchEvent]ev=ACTION_DOWN
 3 06-07 20:09:49.746 27357-27357/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onInterceptTouchEvent]ev=ACTION_DOWN
 4 06-07 20:09:49.746 27357-27357/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->dispatchTouchEvent]ev=ACTION_DOWN
 5 06-07 20:09:49.746 27357-27357/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onInterceptTouchEvent]ev=ACTION_DOWN
 6 06-07 20:09:49.746 27357-27357/com.example.demos I/songzheweiwang: [ViewInner-->dispatchTouchEvent]event=ACTION_DOWN
 7 06-07 20:09:49.746 27357-27357/com.example.demos I/songzheweiwang: [ViewInner-->onTouchEvent]event=ACTION_DOWN
 8 06-07 20:09:49.746 27357-27357/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onTouchEvent]event=ACTION_DOWN
 9 06-07 20:09:49.747 27357-27357/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onTouchEvent]event=ACTION_DOWN
10 06-07 20:09:49.747 27357-27357/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_DOWN
11 06-07 20:09:49.803 27357-27357/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_UP
12 06-07 20:09:49.803 27357-27357/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_UP

这里在activity_event_demo.xml中使用ViewGroupMiddle时添加[android:clickable="true"],将ViewGroupMiddle设置为默认可以处理Touch事件。当设置为false值时,从日志来看,表明ViewGroupMiddle中确实没有处理事件,而是传给了上级。

6, 均为默认值时

当ViewGroupMiddle中onTouchEvent返回默认的super.onTouchEvent时,我们在第三节中分析过ViewInner有能处理和没有能力处理两种情况下的事件处理逻辑,这里笔者不再赘述。现在还有一个结论需要读者验证,就是都在返回默认super.xxx情况下,可以在ViewGroupMiddle中onTouchEvent方法中打印出super.onTouchEvent的值。可以发现,如果ViewGroupMiddle中onTouchEvent方法可以处理事件,则值为true,如果没有处理Touch事件的能力,则会返回false。这一点在第四节中讲过。

六、当触摸其它区域时分析

在前面分析打印log结果的时候,笔者都着重强调了要点击正中心的ViewInner。这是因为点击不同的区域,会产生不同的逻辑处理结果。那么点击区域和事件分发结果有什么样的关系呢?下面将第三节中的例子,3个主要方法都返回默认的super.xxx方法,由外到内依次点击Boss,PM,Team Leader,Programmer四个区域。得到了如下的log信息:

 1 06-07 20:27:44.390 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_DOWN
 2 06-07 20:27:44.391 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_DOWN
 3 06-07 20:27:44.405 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_UP
 4 06-07 20:27:44.405 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_UP
 5
 6 06-07 20:27:48.298 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_DOWN
 7 06-07 20:27:48.299 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupOuter-->dispatchTouchEvent]ev=ACTION_DOWN
 8 06-07 20:27:48.299 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onInterceptTouchEvent]ev=ACTION_DOWN
 9 06-07 20:27:48.299 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onTouchEvent]event=ACTION_DOWN
10 06-07 20:27:48.299 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_DOWN
11 06-07 20:27:48.338 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_UP
12 06-07 20:27:48.339 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_UP
13
14 06-07 20:27:52.681 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_DOWN
15 06-07 20:27:52.681 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupOuter-->dispatchTouchEvent]ev=ACTION_DOWN
16 06-07 20:27:52.681 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onInterceptTouchEvent]ev=ACTION_DOWN
17 06-07 20:27:52.681 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->dispatchTouchEvent]ev=ACTION_DOWN
18 06-07 20:27:52.681 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onInterceptTouchEvent]ev=ACTION_DOWN
19 06-07 20:27:52.682 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onTouchEvent]event=ACTION_DOWN
20 06-07 20:27:52.682 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onTouchEvent]event=ACTION_DOWN
21 06-07 20:27:52.682 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_DOWN
22 06-07 20:27:52.749 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_UP
23 06-07 20:27:52.749 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_UP
24
25 06-07 20:27:57.448 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_DOWN
26 06-07 20:27:57.449 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupOuter-->dispatchTouchEvent]ev=ACTION_DOWN
27 06-07 20:27:57.449 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onInterceptTouchEvent]ev=ACTION_DOWN
28 06-07 20:27:57.449 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->dispatchTouchEvent]ev=ACTION_DOWN
29 06-07 20:27:57.449 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onInterceptTouchEvent]ev=ACTION_DOWN
30 06-07 20:27:57.449 28523-28523/com.example.demos I/songzheweiwang: [ViewInner-->dispatchTouchEvent]event=ACTION_DOWN
31 06-07 20:27:57.449 28523-28523/com.example.demos I/songzheweiwang: [ViewInner-->onTouchEvent]event=ACTION_DOWN
32 06-07 20:27:57.449 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupMiddle-->onTouchEvent]event=ACTION_DOWN
33 06-07 20:27:57.449 28523-28523/com.example.demos I/songzheweiwang: [ViewGroupOuter-->onTouchEvent]event=ACTION_DOWN
34 06-07 20:27:57.450 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_DOWN
35 06-07 20:27:57.514 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->dispatchTouchEvent]ev=ACTION_UP
36 06-07 20:27:57.515 28523-28523/com.example.demos I/songzheweiwang: [EventDemoActivity-->onTouchEvent]event=ACTION_UP

这四次触摸事件的日志结果用空格隔开,分析该log可以发现:当点击Boss区域时,里面的三个控件均未触发事件;当点击PM区域时,Team Leader和Programmer中的没有任何动作;点击Team Leader区域时,只有Programmer没有触发任何事件;当点击Programmer区域时,4个角色均被触发。那么这个结论就很显而易见了:当点击到View系统的某一层时,事件从外往内传递时,只到被点击的那一层为止,不会再派发到其子View中。

结语

到目前为止,Android的事件分发和传递机制就分析完了。本文中Touch事件的3个主要方法返回值均有3种情形,所以会有多种逻辑处理组合。这里选取了中间层ViewGroupMiddle来举例,只是作为代表来分析,笔者完全可以通过其它的组合来分析更多的可能情况。如果分析中有不妥当或者不准确的地方,欢迎来拍砖。

原文地址:https://www.cnblogs.com/andy-songwei/p/10989172.html

时间: 2024-08-03 13:01:30

【朝花夕拾】Android自定义View篇之(五)Android事件分发及传递机制的相关文章

【朝花夕拾】Android自定义View篇之(六)Android事件分发机制(中)从源码分析事件分发逻辑及经常遇到的一些“诡异”现象

前言 转载请注明,转自[https://www.cnblogs.com/andy-songwei/p/11039252.html]谢谢! 在上一篇文章[[朝花夕拾]Android自定义View篇之(五)Android事件分发机制(上)Touch三个重要方法的处理逻辑][下文简称(五),请先阅读完(五)再阅读本文],我们通过示例和log来分析了Android的事件分发机制.这些,我们只是看到了现象,如果要进一步了解事件分发机制,这是不够的,我们还需要透过现象看本质,去研究研究源码.本文将从源码(基

【朝花夕拾】Android自定义View篇之(八)多点触控(上)基础知识

前言 转载请声明,转自[https://www.cnblogs.com/andy-songwei/p/11155259.html],谢谢! 在前面的文章中,介绍了不少触摸相关的知识,但都是基于单点触控的,即一次只用一根手指.但是在实际使用App中,常常是多根手指同时操作,这就需要用到多点触控相关的知识了.多点触控是在Android2.0开始引入的,在现在使用的Android手机上都是支持多点触控的.本系列文章将对常见的多点触控相关的重点知识进行总结,并使用多点触控来实现一些常见的效果,从而达到将

Android焦点事件分发与传递机制

如果您对TouchEvent事件分发机制不太了解的,可以参考我的这篇文章--安卓TounchEvent事件分发机制. 问题:TV端焦点满天飞,如何解决和处理? 记得初入TV开发,以为很简单.TV的这些界面与布局太简单了,分分钟就可以把页面搭建出来,处理好,然后就没有然后了.... 下面我们就从源码来带大家进行安卓TV焦点事件的传递 这里先给出Android系统View的绘制流程: 依次执行View类里面的如下三个方法: measure(int ,int) :测量View的大小 layout(in

【自定义View系列】04--谈谈事件分发

引言:这部分会分三个模块来讲,先讲View对Touch的处理,再讲ViewGroup的事件分发,最后讲如何解决滑动冲突. 我习惯通过在源码中添加注释来理解源码,以下是我提取出来几个重要方法,将不重要的部分删掉,并且添加了中文注释. 一.先从View讲起 如果一个View(比如Button)接收到Touch,那么该Touch事件首先会传入到它的dispatchTouchEvent( )方法,所以我们从这里开始学习View对Touch事件的处理. // 返回值表示Touch事件是否被该View消费

【朝花夕拾】Android自定义View篇之(九)多点触控(下)实践出真知

前言 在上一篇文章中,已经总结了MotionEvent以及多点触控相关的基础理论知识和常用的函数.本篇将通过实现单指拖动图片,多指拖动图片的实际案例来进行练习并实现一些效果,来理解前面的理论知识.要理解本文的代码,需要先掌握上一篇的理论知识,事件处理基础,以及一定的自定义View基础,这些我也在本系列文章的前几篇中讲过,有兴趣的可以按照本系列的顺序依次阅读学习,相信您一定会有不小的收获.. 一.实现单指拖动图片 要实现单指拖动图片,大致思路就是监控手指的ACTION_MOVE事件.手指移动过程中

Android 自定义View实现单击和双击事件

自定义View, 1. 自定义一个Runnable线程TouchEventCountThread ,  用来统计500ms内的点击次数 2. 在MyView中的 onTouchEvent 中调用 上面的线程 3. 自定义一个Handler, 在TouchEventHandler 中 处理 统计到的点击事件, 单击, 双击, 三击, 都可以处理 核心代码如下: public class MyView extends View { ...... // 统计500ms内的点击次数 TouchEvent

android 自定义view,绘制与onTouchEvent事件(一)

绘制 构造方法 自定义view需要继承View类,重写两个构造方法 //用在代码new该view对象,初始化 public MyView(Context context) { super(context); init(); } //一般添加构造--->view放进布局,系统实例化 public MyView(Context context, AttributeSet attrs) { super(context, attrs); init(); } Paint对象 绘制view需要使用Paint

Android自定义View学习笔记04

Android自定义View学习笔记04 好长时间没有写相关的博客了,前几周在帮学姐做毕设,所以博客方面有些耽误.过程中写了一个类似wp的磁贴的view,想再写个配套的layout,所以昨天看了一下自定义viewGroup的相关知识-晚上睡觉想了一下可行性不是很高-代码量还不如直接自己在xml上写来得快,速度上也是个问题.今天看了一下张鸿洋老师的Android 自定义View (三) 圆环交替 等待效果这篇博文,再加上前一段时间看到的一幅图,结合之前写的一个圆形imageView的实现博文And

android自定义View (五)view.requestLayout() 与 invalidate()

一.要点 If in the course of processing the event, the view's bounds may need to be changed, the view will call requestLayout(). Similarly, if in the course of processing the event the view's appearance may need to be changed, the view will call invalida