本文已参与「新人创作礼」活动,一起开启掘金创作之路。
事件分发机制,简而言之就是Android对触摸事件的一系列传递和处理的机制。在了解分发机制之前需要对下面三个方法有个大概的了解:
public boolean dispatchTouchEvent(MotionEvent event)
用来进行事件的分发。如果有事件传递给当前View,那么此方法一定会被调用。返回值受当前View的onTouchEvent(MotionEvent event)和子View的dispatchTouchEvent(MotionEvent event)方法的影响,表示是否消耗当前事件。
public boolean onInterceptTouchEvent(MotionEvent ev)
用来判断是否拦截事件,返回值表示是否拦截当前事件。
public boolean onTouchEvent(MotionEvent event)
用来处理事件,返回值表示是否消耗当前事件,如果不消耗,则在同一事件序列中,当前View无法再次接收到事件。
我们知道Android的View结构是树状结构的,View可以放在一个ViewGroup里,这个ViewGroup又可以放在一个ViewGroup里,那么当我们点击一个嵌套的结构,事件传递是怎样的呢?
为此,我们用代码来实现一下。
1.创建BaseViewGroup作为底层ViewGroup,代码如下:
public class BaseViewGroup extends FrameLayout {
public BaseViewGroup(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.d("ViewEvent", "BaseViewGroup-dispatchTouchEvent-"+ev.getAction());
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.d("ViewEvent", "BaseViewGroup-onInterceptTouchEvent-"+ev.getAction());
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d("ViewEvent", "BaseViewGroup-onTouchEvent-"+event.getAction());
return super.onTouchEvent(event);
}
}
2.创建TopViewGroup作为顶层ViewGroup,代码如下:
public class TopViewGroup extends FrameLayout {
public TopViewGroup(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.d("ViewEvent", "TopViewGroup-dispatchTouchEvent-"+ev.getAction());
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.d("ViewEvent", "TopViewGroup-onInterceptTouchEvent-"+ev.getAction());
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d("ViewEvent", "TopViewGroup-onTouchEvent-"+event.getAction());
return super.onTouchEvent(event);
}
}
3.创建MyView作为最上层的View,代码如下:
public class MyView extends View {
public MyView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.d("ViewEvent", "MyView-dispatchTouchEvent-"+ev.getAction());
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d("ViewEvent", "MyView-onTouchEvent-"+event.getAction());
return super.onTouchEvent(event);
}
}
4.布局自定义View及ViewGroup,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<com.droidyu.viewsystem._3_event.BaseViewGroup xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="400dp"
android:layout_height="400dp"
android:background="#1f1"
tools:context="._3_event.ViewEventActivity">
<com.droidyu.viewsystem._3_event.TopViewGroup
android:background="#ff1"
android:layout_width="300dp"
android:layout_height="300dp" >
<com.droidyu.viewsystem._3_event.MyView
android:background="#f11"
android:layout_width="200dp"
android:layout_height="200dp" />
</com.droidyu.viewsystem._3_event.TopViewGroup>
</com.droidyu.viewsystem._3_event.BaseViewGroup>
最终,视图结构如下所示:

运行程序,点击红色的MyView,然后查看Log日志如下:
从Log日志可以看出,正常情况下,
事件的传递顺序是:
BaseViewGroup -> TopViewGroup -> MyView 的dispatchTouchEvent和onInterceptTouchEvent方法
事件处理顺序是:
MyView -> TopViewGroup -> BaseViewGroup 的onTouchEvent方法
此时将BaseViewGroup的onInterceptTouchEvent返回值改为true,再次点击红色的MyView,然后查看Log日志如下:
从Log日志可以看出,BaseViewGroup拦截事件之后,就直接处理事件,不会再将事件向子View传递。
还原代码,将TopViewGroup的onInterceptTouchEvent返回值改为true,再次点击红色的MyView,然后查看Log日志如下:
从Log日志可以看出,事件从BaseViewGroup传递到TopViewGroup,TopViewGroup拦截事件之后,就直接处理事件,不会再将事件向子View传递,处理后会不消耗,返回给BaseViewGroup再处理。
还原代码,将MyView的onTouchEvent返回值改为true,再次点击红色的MyView,然后查看Log日志如下:
从Log日志可以看出,事件传递到MyView,MyView处理事件之后,就不再向上传递。
还原代码,将TopViewGroup的onTouchEvent返回值改为true,再次点击红色的MyView,然后查看Log日志如下:
从Log日志可以看出,事件传递到MyView,MyView处理事件之后,又向上传递到TopViewGroup处理,TopViewGroup处理事件之后,就不再向上传递。
至此相信你对View的事件传递和处理有了一个更直观的认识,更多的讲解将在后续文章中更新,敬请期待。。。
源码见Github
学习更多知识,请关注我的个人博客:droidYu