简述
事件的传递主要有三个方法:
- dispatchTouchEvent(),事件分发
- onInterceptTouchEvent(),事件拦截
- onTouchEvent(),事件消费
其中onInterceptTouchEvent只存在 ViewGroup 中。这三个方法都有返回值,返回true表示该事件被消费,事件传递终止,返回 false,事件继续传递。事件分成好几种类型,我们常用的有三种,从手指按下移动到抬起依次为:ACTION_DOWN--->ACTION_MOVE--->ACTION_UP。
事件的传递过程
事件的传递在我们手指按下(ACTION_DOWN)的瞬间发生,如果手指有移动会触发若干个移动事件(ACTION_MOVE),当手指抬起来时会触发ACTION_UP事件,这样为一个事件序列。如下示例,一个Activity中放有一个ViewGroup,ViewGroup放有一个View,其中ViewGroup和View都是我们自定义的,分别继承自 ViewGroup 和 View 的子类,代码如下:
public class TouchEventActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activty_touch_event);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.e(getClass().getSimpleName(), "这是 Activity 的 dispatchTouchEvent");
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
Log.e(getClass().getSimpleName(), "这是 Activity 的 onTouchEvent");
return super.onTouchEvent(ev);
}
}
复制代码
布局文件如下:
其中:
public class CustomLinearLayout extends LinearLayout {
...
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.e(getClass().getSimpleName(), "这是 ViewGroup 的 dispatchTouchEvent");
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.e(getClass().getSimpleName(), "这是 ViewGroup 的 onInterceptTouchEvent");
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
Log.e(getClass().getSimpleName(), "这是 ViewGroup 的 onTouchEvent");
return super.onTouchEvent(ev);
}
}
复制代码
public class CustomTextView extends TextView {
...
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.e(getClass().getSimpleName(), "这是 View 的 dispatchTouchEvent");
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
Log.e(getClass().getSimpleName(), "这是 View 的 onTouchEvent");
return super.onTouchEvent(ev);
}
}
复制代码
UI效果如下:
运行并点击自定义TextView,打印日志如下:
根据日志可知,事件的传递是由上往下传递的,也就是依次通过 Activity --> ViewGroup --> View。按下的瞬间 ACTION_DOWN 触发,Activity 的 dispatchTouchEvent 会先调用。Activity 中的 dispatchTouchEvent 会调用 ViewGroup 或者 View 的 dispatchTouchEvent,当 ViewGroup 或者 View 返回 false 时才会调用 Activty 本身的 onTouchEvent 方法。这里有个知识点,就是 ViewGroup 的 dispatchTouchEvent 会调用自身的 onInterceptTouchEvent,这一点跟 Activity 中有点不一样,因为 Activity 中并没有事件拦截方法,如果 ViewGroup 的 onInterceptTouchEvent 事件拦截方法返回 true,那么 View 中的 dispatchTouchEvent 不会被调用,反而会调用 ViewGroup 的 onTouchEvent ,那么该事件(ACTION_DOWN)传递结束。
如果返回的是 false(默认就是返回 false),那么 View 中的 dispatchTouchEvent 会被调用,因为 View 是最底层的控件,事件无法继续往下传递,只能自身消费,所以 dispatchTouchEvent 又会调用 onTouchEvent 方法,在我们这个例子中,onTouchEvent 返回的默认值是 false,也就是没有消费该事件。
我们知道事件是从上往下传递的,那么当事件传递到最底层的 View 并且事件没有被消费,又该如何处理呢?其实根据上面的日志我们可以看出,事件被一层一层的向上抛了,接下来会执行 ViewGroup 中的 onTouchEvent ,如果返回 true 的话,事件传递停止,如果还是一样返回默认值 false 的话,Activity 中的 onTouchEvent 会被调用,到此 ACTION_DOWN 事件的传递结束这就是一次完整的事件传递过程。
事件传递的流程图:
细心的读者可能会问,既然事件的传递结束了,为什么 Activity 的 dispatchTouchEvent, onTouchEvent 又被执行了两次呢(日志打印出来的)?
没错,确实是执行了,刚才我们说过了:手指按下移动到抬起,会执行一次 ACTION_DOWN,若干次 ACTION_MOVE,一次 ACTION_UP ,被执行了两次是因为执行了一次 ACTION_MOVE 和一次 ACTION_UP 事件,也就是还有两次完整的事件传递过程,但是我们发现后面这两次跟 ACTION_DOWN 不一样,只调用两次 Activity 的 dispatchTouchEvent,onTouchEvent 方法。这是因为,Android 本身的事件传递机制就是这样的,我们把手指按下抬起所发生的事件传递称为一个事件序列。看似3个或3个以上独立事件组成,其实不然,它们还是有联系的,因为当 dispatchTouchEvent 在进行事件分发的时候,只有前一个 action 返回 true,才会触发下一个 action,也就是说刚才 Activity 的 dispatchTouchEvent 的方法中,ViewGroup 或者 view 中的 dispatchTouchEvent 返回的是默认值 false,接下来 ACTION_MOVE,ACTION_UP 两个事件并不会触发 ViewGroup 的 dispatchTouchEvent,反而是直接执行自身的 onTouchEvent。这告诉我们,如果一个事件序列的ACTION_DOWN事件没有消费掉,那么该事件序列的 ACTION_MOVE,ACTION_UP 并不会被执行。
接下来我们稍微修改一下代码,把 CustomTextView 的 onTouchEvent 改成返回 true,如下:
@Override
public boolean onTouchEvent(MotionEvent ev) {
Log.e(getClass().getSimpleName(), "这是 View 的 onTouchEvent");
return true;
}
复制代码
运行点击,日志如下:
CustomTextView 的 onTouchEvent 消费了事件,所以该序列的后续事件都会完整的传递到 CustomTextView 中,并且都会在改方法中终止事件的传递。
我们再来看看把 CustomTextView 的 dispatchTouchEvent 也改成直接返回 true,是个什么情况,完整的代码如下:
public class CustomTextView extends TextView {
...
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Log.e(getClass().getSimpleName(), "这是View的--->dispatchTouchEvent");
return true;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e(getClass().getSimpleName(), "这是View的--->onTouchEvent");
return true;
}
}
复制代码
执行点击,日志如下:
我们看到每次事件都会传递到 View 的 dispatchTouchEvent,但是 onTouchEvent 并不会被执行,关键代码就是 View 中的 dispatchTouchEvent 返回值不一样:
return super.dispatchTouchEvent(ev);
return true;
复制代码
因为 onTouchEvent 是在 super.dispatchTouchEvent 中执行的,所以我们虽然返回了 true,每次事件都会传递过来但是并不会执行 onTouchEvent。
原文地址:http://blog.youkuaiyun.com/sonnyjack/article/details/79376683