事件分发的三个函数
事件的分发
dispatchTouchEvent()
事件的拦截onInterceptTouchEvent()
事件的处理(消费)onTouchEvent()
事件分发的对象
被分发的对象是那些?被分发的对象是用户触摸屏幕而产生的点击事件,事件主要包括:按下、滑动、抬起与取消。这些事件被封装成MotionEvent对象。
MotionEvent.ACTION_DOWN 在屏幕按下时 MotionEvent.ACTION_MOVE 在屏幕上滑动时 MotionEvent.ACTION_UP 在屏幕抬起时 MotionEvent.ACTION_CANCLE 滑动超出控件边界时
分发事件的组件
分发事件的组件,也称为分发事件者,包括Activity、View和ViewGroup。它们三者的一般结构为:
首先,我们需要了解事件处理中的几个方法:
1、在ViewGroup中,事件分为dispatchTouchEvent(事件的分发),onInterceptTouchEvent(事件的拦截),onTouchEvent(事件的处理)。
2、在View中,事件分为dispatchTouchEvent(事件的分发),onTouchEvent(事件的处理)。
下面是demo的界面结构,它是由两个自定义的ViewGroup和一个自定义的View组成,并分别重写了它们的以上几个方法。
其中 MyViewGroupA
代码如下:
public class MyViewGroupA extends LinearLayout {public MyViewGroupA(Context context) {super(context);}public MyViewGroupA(Context context, AttributeSet attrs) {super(context, attrs);}@Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {switch (ev.getAction()){case MotionEvent.ACTION_DOWN:Log.i("MyViewGroupA","dispatchTouchEvent_ACTION_DOWN");break;case MotionEvent.ACTION_MOVE:Log.i("MyViewGroupA","dispatchTouchEvent_ACTION_MOVE");break;case MotionEvent.ACTION_UP:Log.i("MyViewGroupA","dispatchTouchEvent_ACTION_UP");break;}return super.dispatchTouchEvent(ev);}@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {switch (ev.getAction()){case MotionEvent.ACTION_DOWN:Log.i("MyViewGroupA","onInterceptTouchEvent_ACTION_DOWN");break;case MotionEvent.ACTION_MOVE:Log.i("MyViewGroupA","onInterceptTouchEvent_ACTION_MOVE");break;case MotionEvent.ACTION_UP:Log.i("MyViewGroupA","onInterceptTouchEvent_ACTION_UP");break;}return super.onInterceptTouchEvent(ev);}@Overridepublic boolean onTouchEvent(MotionEvent event) {switch (event.getAction()){case MotionEvent.ACTION_DOWN:Log.i("MyViewGroupA","onTouchEvent_ACTION_DOWN");break;case MotionEvent.ACTION_MOVE:Log.i("MyViewGroupA","onTouchEvent_ACTION_MOVE");break;case MotionEvent.ACTION_UP:Log.i("MyViewGroupA","onTouchEvent_ACTION_UP");break;}return super.onTouchEvent(event);}
}
MyViewGroupB
代码如下:
public class MyViewGroupB extends LinearLayout {public MyViewGroupB(Context context) {super(context);}public MyViewGroupB(Context context, AttributeSet attrs) {super(context, attrs);}@Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {switch (ev.getAction()){case MotionEvent.ACTION_DOWN:Log.i("MyViewGroupB","dispatchTouchEvent_ACTION_DOWN");break;case MotionEvent.ACTION_MOVE:Log.i("MyViewGroupB","dispatchTouchEvent_ACTION_MOVE");break;case MotionEvent.ACTION_UP:Log.i("MyViewGroupB","dispatchTouchEvent_ACTION_UP");break;}return super.dispatchTouchEvent(ev);}@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {switch (ev.getAction()){case MotionEvent.ACTION_DOWN:Log.i("MyViewGroupB","onInterceptTouchEvent_ACTION_DOWN");break;case MotionEvent.ACTION_MOVE:Log.i("MyViewGroupB","onInterceptTouchEvent_ACTION_MOVE");break;case MotionEvent.ACTION_UP:Log.i("MyViewGroupB","onInterceptTouchEvent_ACTION_UP");break;}return super.onInterceptTouchEvent(ev);}@Overridepublic boolean onTouchEvent(MotionEvent event) {switch (event.getAction()){case MotionEvent.ACTION_DOWN:Log.i("MyViewGroupB","onTouchEvent_ACTION_DOWN");break;case MotionEvent.ACTION_MOVE:Log.i("MyViewGroupB","onTouchEvent_ACTION_MOVE");break;case MotionEvent.ACTION_UP:Log.i("MyViewGroupB","onTouchEvent_ACTION_UP");break;}return super.onTouchEvent(event);}
}
MyView
代码如下:
public class MyView extends View {public MyView(Context context) {super(context);}public MyView(Context context, AttributeSet attrs) {super(context, attrs);}@Overridepublic boolean dispatchTouchEvent(MotionEvent event) {switch (event.getAction()){case MotionEvent.ACTION_DOWN:Log.i("MyView","dispatchTouchEvent_ACTION_DOWN");break;case MotionEvent.ACTION_MOVE:Log.i("MyView","dispatchTouchEvent_ACTION_MOVE");break;case MotionEvent.ACTION_UP:Log.i("MyView","dispatchTouchEvent_ACTION_UP");break;}return super.dispatchTouchEvent(event);}@Overridepublic boolean onTouchEvent(MotionEvent event) {switch (event.getAction()){case MotionEvent.ACTION_DOWN:Log.i("MyView","onTouchEvent_ACTION_DOWN");break;case MotionEvent.ACTION_MOVE:Log.i("MyView","onTouchEvent_ACTION_MOVE");break;case MotionEvent.ACTION_UP:Log.i("MyView","onTouchEvent_ACTION_UP");break;}return super.onTouchEvent(event);}
}
我们说过,事件传递是由上到下的,所以最外层的View首先对事件进行操作。而我们最外层是Activity,所以事件也是从这里开始。 Activity代码如下:
public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}@Overridepublic boolean dispatchTouchEvent(MotionEvent event) {switch (event.getAction()){case MotionEvent.ACTION_DOWN:Log.i("Activity","dispatchTouchEvent_ACTION_DOWN");break;case MotionEvent.ACTION_MOVE:Log.i("Activity","dispatchTouchEvent_ACTION_MOVE");break;case MotionEvent.ACTION_UP:Log.i("Activity","dispatchTouchEvent_ACTION_UP");break;}return super.dispatchTouchEvent(event);}@Overridepublic boolean onTouchEvent(MotionEvent event) {switch (event.getAction()){case MotionEvent.ACTION_DOWN:Log.i("Activity","onTouchEvent_ACTION_DOWN");break;case MotionEvent.ACTION_MOVE:Log.i("Activity","onTouchEvent_ACTION_MOVE");break;case MotionEvent.ACTION_UP:Log.i("Activity","onTouchEvent_ACTION_UP");break;}return super.onTouchEvent(event);}
}
现在我们通过触摸MyView开始进行分析。虽然dispatchTouchEvent是事件开始的第一步,但是在开发中,我们通常很少改写它,所以我们下面只讨论其他两个方法。 1、对以上方法均不作处理,都返回super。这意味着我们既不拦截,也不消费。
大家看输出结果:
I/Activity: dispatchTouchEvent_ACTION_DOWNI/MyViewGroupA: dispatchTouchEvent_ACTION_DOWN
I/MyViewGroupA: onInterceptTouchEvent_ACTION_DOWNI/MyViewGroupB: dispatchTouchEvent_ACTION_DOWN
I/MyViewGroupB: onInterceptTouchEvent_ACTION_DOWNI/MyView: dispatchTouchEvent_ACTION_DOWN
I/MyView: onTouchEvent_ACTION_DOWNI/MyViewGroupB: onTouchEvent_ACTION_DOWN
I/MyViewGroupA: onTouchEvent_ACTION_DOWN
I/Activity: onTouchEvent_ACTION_DOWNI/Activity: dispatchTouchEvent_ACTION_MOVE
I/Activity: onTouchEvent_ACTION_MOVEI/Activity: dispatchTouchEvent_ACTION_UP
I/Activity: onTouchEvent_ACTION_UP
结合输出结果,我们可以总结出以下的结论:
结合流程图,不难发现,如果我对事件既不拦截,也不消费,当触发ACTION_DOWN
的时候,事件会经过Activity——MyViewGroupA——MyViewGroupB——MyView一层层的向下进行dispatchTouchEvent
(分发)—onInterceptTouchEvent
(拦截)调用。当到达最底层MyView后,开始触发消费操作,因为我均不消费,ACTION_DOWN
将由底层一层层向上冒,移交上层处理。当抵达最上层Activity后,说明下层均不消费,之后触发的ACTION_MOVE
和ACTION_UP
将不再向下层分发传递,直接交由Activity分发给自己进行处理。
2、我们将MyVIewGroupB
的onInterceptTouchEvent
返回值改为true,其他均是super。这意味着仅仅MyViewGroupB
进行事件拦截,但均无消费
输出结果如下:
I/Activity: dispatchTouchEvent_ACTION_DOWNI/MyViewGroupA: dispatchTouchEvent_ACTION_DOWN
I/MyViewGroupA: onInterceptTouchEvent_ACTION_DOWNI/MyViewGroupB: dispatchTouchEvent_ACTION_DOWN
I/MyViewGroupB: onInterceptTouchEvent_ACTION_DOWNI/MyViewGroupB: onTouchEvent_ACTION_DOWN
I/MyViewGroupA: onTouchEvent_ACTION_DOWN
I/Activity: onTouchEvent_ACTION_DOWNI/Activity: dispatchTouchEvent_ACTION_MOVE
I/Activity: onTouchEvent_ACTION_MOVEI/Activity: dispatchTouchEvent_ACTION_UP
I/Activity: onTouchEvent_ACTION_UP
结合输出结果,总结如下:
当触发ACTION_DOWN
的时候,事件依然是从Activity开始一层层向下传递,当传递到MyViewGroupB
时,因为进行了事件拦截,所以执行完onInterceptTouchEvent
后不再向下传递,而是直接交由MyViewGroupB
的onTouchEvent
进行消费处理。由于我们是只拦截,不消费,所以事件向上传递,交由上层处理,最终回到Activity。之后触发的ACTION_MOVE
和ACTION_UP
也不再向下传递,直接交由Activity分发给自己处理。
3、我们还是将MyViewGroupB
的onInterceptTouchEvent
返回super,但是将他的onTouchEvent
返回true。这意味着我们不拦截,但是由MyViewGroupB
进行事件处理。
输出结果如下:
I/Activity: dispatchTouchEvent_ACTION_DOWNI/MyViewGroupA: dispatchTouchEvent_ACTION_DOWN
I/MyViewGroupA: onInterceptTouchEvent_ACTION_DOWNI/MyViewGroupB: dispatchTouchEvent_ACTION_DOWN
I/MyViewGroupB: onInterceptTouchEvent_ACTION_DOWNI/MyView: dispatchTouchEvent_ACTION_DOWN
I/MyView: onTouchEvent_ACTION_DOWN
I/MyViewGroupB: onTouchEvent_ACTION_DOWNI/Activity: dispatchTouchEvent_ACTION_MOVE
I/MyViewGroupA: dispatchTouchEvent_ACTION_MOVE
I/MyViewGroupA: onInterceptTouchEvent_ACTION_MOVE
I/MyViewGroupB: dispatchTouchEvent_ACTION_MOVE
I/MyViewGroupB: onTouchEvent_ACTION_MOVEI/Activity: dispatchTouchEvent_ACTION_UP
I/MyViewGroupA: dispatchTouchEvent_ACTION_UP
I/MyViewGroupA: onInterceptTouchEvent_ACTION_UP
I/MyViewGroupB: dispatchTouchEvent_ACTION_UP
I/MyViewGroupB: onTouchEvent_ACTION_UP
结合输出结果,总结如下:
可以看出,当触发ACTION_DOWN
的时候,事件的分发传递过程和1的时候一样,从Activity开始一层层向下传递,最终传递到最底层MyView,触发消费操作,然后MyView将消费操作移交上层处理,然后到达MyViewGroupB
的onTouchEvent
,并且进行了消费处理,事件处理到此不在向上移交。当触发ACTION_MOVE
和ACTION_UP
操作时,事件依然需要由Activity开始向下分发传递,但是当传递到MyViewGroupB
后,由于其消费了ACTION_DOWN
,事件将不再继续向下分发,而是直接由MyViewGroupB
分发给自己的onTouchEvent
进行继续处理。事件处理也不再向上移交。
4、将MyViewGroupB
的onInterceptTouchEvent
和onTouchEvent
的返回值均改为true。这意味着既拦截,又消费。
输出结果如下:
I/Activity: dispatchTouchEvent_ACTION_DOWNI/MyViewGroupA: dispatchTouchEvent_ACTION_DOWN
I/MyViewGroupA: onInterceptTouchEvent_ACTION_DOWNI/MyViewGroupB: dispatchTouchEvent_ACTION_DOWN
I/MyViewGroupB: onInterceptTouchEvent_ACTION_DOWNI/MyViewGroupB: onTouchEvent_ACTION_DOWNI/Activity: dispatchTouchEvent_ACTION_MOVE
I/MyViewGroupA: dispatchTouchEvent_ACTION_MOVE
I/MyViewGroupA: onInterceptTouchEvent_ACTION_MOVE
I/MyViewGroupB: dispatchTouchEvent_ACTION_MOVE
I/MyViewGroupB: onTouchEvent_ACTION_MOVEI/Activity: dispatchTouchEvent_ACTION_UP
I/MyViewGroupA: dispatchTouchEvent_ACTION_UP
I/MyViewGroupA: onInterceptTouchEvent_ACTION_UP
I/MyViewGroupB: dispatchTouchEvent_ACTION_UP
I/MyViewGroupB: onTouchEvent_ACTION_UP
结合输出结果,总结如下:
当触发ACTION_DOWN
的时候,依然从Activity开始向下传递,当到达MyViewGroupB
的是,因为在onInterceptTouchEvent
进行了拦截操作,因此不再继续向下分发传递,而是交由MyViewGroupB
的onTouchEvent
进行处理消费。MyViewGroupB
的onTouchEvent
返回的是true,说明它决定对ACTION_DOWN
进行处理,因此事件也就不再移交上层处理。当触发ACTION_MOVE
和ACTION_UP
的时候,事件还是从Activity开始向下传递,当到达MyViewGroupB
的时候,由于之前进行了拦截操作,因此,MyViewGroupB
直接将事件分发给自己的onTouchEvent
进行处理,不在向下分发传递。事件处理也不再向上层移交。
Android Framework底层原理篇:https://qr18.cn/AQpN4J
Android 性能优化篇:https://qr18.cn/FVlo89
Android 车载篇:https://qr18.cn/F05ZCM
Android 音视频篇:https://qr18.cn/Ei3VPD
Jetpack全家桶篇(内含Compose):https://qr18.cn/A0gajp
Kotlin 篇:https://qr18.cn/CdjtAF
Flutter 篇:https://qr18.cn/DIvKma
OkHttp 篇:https://qr18.cn/DzrmMB
Gradle 篇:https://qr18.cn/Cw0pBD
Android 八大知识体:https://qr18.cn/CyxarU
Android 核心笔记:https://qr21.cn/CaZQLo
Android 面试题锦:https://qr18.cn/CKV8OZ