事件分发流程对于咱们开发者来说,有什么做用?php
引用官方介绍: Object used to report movement (mouse, pen, finger, trackball) events. Motion events may hold either absolute or relative movements and other data, depending on the type of device.java
大体意思就是用于报告移动(鼠标,笔,手指,轨迹球)事件的对象。运动事件能够保持绝对或相对运动以及其余数据,具体取决于设备的类型。android
在事件分发中,典型经常使用的事件总共有三个:ide
通常一个事件的完整流程是 ACTION_DOWN ---> ACTION_MOVE ---> ACTION_UP源码分析
经常使用方法 | 含义 |
---|---|
getX/Y() | 点击事件相对于 当前 View 左上角 的 x/y 轴距离 |
getRawX/Y() | 点击事件相对于 手机屏幕左上角 的 x/y 轴距离 |
public boolean dispatchTouchEvent(MotionEvent ev)
布局
用于事件分发,将触摸事件向下传递给目标视图,若是它自己就是目标视图,则传递给本身来处理事件。返回结果受本身的 onTouchEvent 和下级 View 的 dispatchTouchEvent 方法影响。post
public boolean onInterceptTouchEvent(MotionEvent ev)
学习
在 dispatchTouchEvent 中调用。是否进行事件拦截,若是返回 false ,则表明不拦截事件;若是返回事件为 true,则拦截事件,而且此事件的后续事件都交给本身来处理,不会再调用此方法询问是否拦截。this
只有 ViewGroup 有这个方法,默认返回 false,不拦截。spa
public boolean onTouchEvent(MotionEvent ev)
在 dispatchTouchEvent 中调用。用来处理事件,返回 true 表明消费事件;返回 false 表明不消费事件。
表明三者关系的伪代码:(摘抄于《Android艺术开发探索》第三章)
//伪代码,解释 dispatchTouchEvent 和 onInterceptTouchEvent 以及 onTouchEvent 的调用关系
public boolean dispatchTouchEvent(MotionEvent ev){
boolean consume;
if(onInterceptTouchEvent(ev)){
consume = onTouchEvent(ev);
}else{
consume = childView.dispatchTouchEvent(ev);
}
return consume ;
}
复制代码
经过上面的伪代码,咱们能够大体的了解事件的传递规则:对于一个根 ViewGroup 来讲,点击事件产生后,首先会传递给它,这时它的 dispatchTouchEvent 就会被调用,若是它的 onInterceptTouchEvent 方法返回 true,就表示要拦截事件,接着事件就交给这个 ViewGroup 的 onTouchEvent 来处理,若是 onInterceptTouchEvent 返回 false,表示不拦截事件,当前事件就会传递给它的子元素,接着子元素的 dispatchTouchEvent 方法就会被调用,如此反复直到事件被最终处理。
三个方法在具体项目中的具体的调用流程是什么呢?咱们用个 Demo 来演示。
都只是简单重写这三个方法,并打印日志。
public class Group1 extends FrameLayout {
public static final String TAG = "----------";
//......代码省略......
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.i(TAG, "dispatchTouchEvent-----------------"+ Util.getMotionEvent(ev) + this.getClass().getSimpleName());
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.i(TAG, "onInterceptTouchEvent-----------------" + Util.getMotionEvent(ev)+ this.getClass().getSimpleName());
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.i(TAG, "onTouchEvent-----------------" + Util.getMotionEvent(event)+ this.getClass().getSimpleName());
return super.onTouchEvent(event);
}
}
复制代码
View 的代码:
public class View1 extends View {
public static final String TAG = "----------";
//......代码省略......
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Log.i(TAG,"dispatchTouchEvent-----------------" + Util.getMotionEvent(event)+ this.getClass().getSimpleName());
return super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.i(TAG,"onTouchEvent-----------------"+ Util.getMotionEvent(event) + this.getClass().getSimpleName());
return super.onTouchEvent(event);
}
}
复制代码
Util 类代码(打印出事件的名称):
class Util {
static String getMotionEvent(MotionEvent motionEvent) {
int action = motionEvent.getAction();
if (action == MotionEvent.ACTION_DOWN) {
return "ACTION_DOWN----";
} else if (action == MotionEvent.ACTION_UP) {
return "ACTION_UP------";
} else if (action == MotionEvent.ACTION_MOVE) {
return "ACTION_MOVE----";
} else if (action == MotionEvent.ACTION_CANCEL) {
return "ACTION_CANCEL--";
} else {
return String.valueOf(motionEvent);
}
}
}
复制代码
MainActivity的代码:
public class MainActivity extends AppCompatActivity {
public static final String TAG = "----------";
//......代码省略......
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.i(TAG, "dispatchTouchEvent-----------------" + com.sjc.eventdispatch.Util.getMotionEvent(ev) + this.getClass().getSimpleName());
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.i(TAG, "onTouchEvent-----------------" + com.sjc.eventdispatch.Util.getMotionEvent(event) + this.getClass().getSimpleName());
return super.onTouchEvent(event);
}
}
复制代码
MainActivity中的 xml 文件:
<?xml version="1.0" encoding="utf-8"?>
<com.sjc.eventdispatch.Group1 xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent">
<com.sjc.eventdispatch.View1 android:id="@+id/view1" android:layout_width="200dp" android:layout_height="300dp" android:layout_gravity="center" android:background="@color/colorPrimaryDark" />
</com.sjc.eventdispatch.Group1>
复制代码
具体页面呈现:
//Log日志
----------: dispatchTouchEvent--------ACTION_DOWN----MainActivity
----------: dispatchTouchEvent--------ACTION_DOWN----Group1
----------: onInterceptTouchEvent--------ACTION_DOWN----Group1
----------: dispatchTouchEvent--------ACTION_DOWN----View1
----------: onTouchEvent--------ACTION_DOWN----View1
----------: onTouchEvent--------ACTION_DOWN----Group1
----------: onTouchEvent--------ACTION_DOWN----MainActivity
----------: dispatchTouchEvent--------ACTION_MOVE----MainActivity
----------: onTouchEvent--------ACTION_MOVE----MainActivity
----------: dispatchTouchEvent--------ACTION_UP------MainActivity
----------: onTouchEvent--------ACTION_UP------MainActivity
复制代码
从事件的传递过程,咱们能够看出,当用户触摸屏幕进行操做时,事件先传递给 Activity,而后经过 dispatchTouchEvent
分发给跟布局 Group1 ,Group1 进行事件分发,先进行调用本身的 onInterceptTouchEvent
进行询问是否拦截(默认不拦截),而后就传递给当前点击的子控件 View1,View1 经过本身的 dispatchTouchEvent
方法分发给本身,调用本身的 onTouchEvent
方法。View1 默认不消费事件,事件就传递给了Group1 本身的 onTouchEvent 方法,因为 Group1 也不消费事件,事件就回传给了 Activity,最终 Activity 的 onTouchEvent 接收了事件,而且后续事件也是由它直接接收。
结论:
- 若是一个控件不消费传递过来的 DOWN 事件,那么后续事件不会传递给它。
- 若是一个点击区域的全部控件都不消费事件,那么这个事件最终会传递个 Activity 。
模拟二:
//log日志
----------: dispatchTouchEvent--------ACTION_DOWN----MainActivity
----------: dispatchTouchEvent--------ACTION_DOWN----Group1
----------: onInterceptTouchEvent--------ACTION_DOWN----Group1
----------: dispatchTouchEvent--------ACTION_DOWN----View1
----------: onTouchEvent--------ACTION_DOWN----View1
----------: dispatchTouchEvent--------ACTION_MOVE----MainActivity
----------: dispatchTouchEvent--------ACTION_MOVE----Group1
----------: onInterceptTouchEvent--------ACTION_MOVE----Group1
----------: dispatchTouchEvent--------ACTION_MOVE----View1
----------: onTouchEvent--------ACTION_MOVE----View1
----------: dispatchTouchEvent--------ACTION_UP------MainActivity
----------: dispatchTouchEvent--------ACTION_UP------Group1
----------: onInterceptTouchEvent--------ACTION_UP------Group1
----------: dispatchTouchEvent--------ACTION_UP------View1
----------: onTouchEvent--------ACTION_UP------View1
复制代码
典型的点击事件传递过程,用户点击一个按钮,按钮来响应操做,事件被这个按钮消费。而且每次事件传递过来时,Group1 都会调用 onInterceptTouchEvent 方法来进行检查是否拦截。
模拟三:
//Log日志
----------: dispatchTouchEvent--------ACTION_DOWN----MainActivity
----------: dispatchTouchEvent--------ACTION_DOWN----Group1
----------: onInterceptTouchEvent--------ACTION_DOWN----Group1
----------: onTouchEvent--------ACTION_DOWN----Group1
----------: onTouchEvent--------ACTION_DOWN----MainActivity
----------: dispatchTouchEvent--------ACTION_MOVE----MainActivity
----------: onTouchEvent--------ACTION_MOVE----MainActivity
----------: dispatchTouchEvent--------ACTION_UP------MainActivity
----------: onTouchEvent--------ACTION_UP------MainActivity
复制代码
//Log日志
----------: dispatchTouchEvent--------ACTION_DOWN----MainActivity
----------: dispatchTouchEvent--------ACTION_DOWN----Group1
----------: onInterceptTouchEvent--------ACTION_DOWN----Group1
----------: onTouchEvent--------ACTION_DOWN----Group1
----------: dispatchTouchEvent--------ACTION_MOVE----MainActivity
----------: dispatchTouchEvent--------ACTION_MOVE----Group1
----------: onTouchEvent--------ACTION_MOVE----Group1
----------: dispatchTouchEvent--------ACTION_UP------MainActivity
----------: dispatchTouchEvent--------ACTION_UP------Group1
----------: onTouchEvent--------ACTION_UP------Group1
复制代码
Group1 的 onInterceptTouchEvent 返回 true 拦截事件,它的 onTouchEvent 被调用。onTouchEvent 返回 true 表明事件被消费,后续事件都传递给它来处理;onTouchEvent 返回 false 表明事件没有消费,事件向上传递给 Activity ,后续事件都不会传递给 Group1 和 View1 来处理。
结论:
- ViewGroup 一旦拦截事件后,后续事件就会交给它来处理,而且不会再调用 onInterceptTouchEvent 方法询问是否拦截。
- 再次验证了
若是一个控件不消费传递过来的 DOWN 事件,那么后续事件不会传递给它
。
事件序列的非人为的提早结束。
模拟条件:
模拟动做: 点击 View1 ;
public class Group1 extends FrameLayout {
public static final String TAG = "----------";
//......代码省略......
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.i(TAG, "onInterceptTouchEvent--------" + Util.getMotionEvent(ev) + this.getClass().getSimpleName());
boolean intercept;
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
intercept = false;
break;
case MotionEvent.ACTION_MOVE:
intercept = true;
break;
case MotionEvent.ACTION_UP:
intercept = false;
break;
default:
intercept = false;
break;
}
return intercept;
}
}
复制代码
//log日志
----------: dispatchTouchEvent--------ACTION_DOWN----MainActivity
----------: dispatchTouchEvent--------ACTION_DOWN----Group1
----------: onInterceptTouchEvent--------ACTION_DOWN----Group1
----------: dispatchTouchEvent--------ACTION_DOWN----View1
----------: onTouchEvent--------ACTION_DOWN----View1
----------: dispatchTouchEvent--------ACTION_MOVE----MainActivity
----------: dispatchTouchEvent--------ACTION_MOVE----Group1
----------: onInterceptTouchEvent--------ACTION_MOVE----Group1
----------: dispatchTouchEvent--------ACTION_CANCEL--View1
----------: onTouchEvent--------ACTION_CANCEL--View1
----------: dispatchTouchEvent--------ACTION_MOVE----MainActivity
----------: dispatchTouchEvent--------ACTION_MOVE----Group1
----------: onTouchEvent--------ACTION_MOVE----Group1
----------: onTouchEvent--------ACTION_MOVE----MainActivity
----------: dispatchTouchEvent--------ACTION_UP------MainActivity
----------: dispatchTouchEvent--------ACTION_UP------Group1
----------: onTouchEvent--------ACTION_UP------Group1
----------: onTouchEvent--------ACTION_UP------MainActivity
复制代码
在这个事件流中,View1 首先消费了 DOWN 事件。当用户移动,产生了 MOVE 事件,因为 Group1 的 onInterceptTouchEvent 方法在 MOVE 事件中返回 true,表示开始拦截事件,MOVE 事件及后面的 UP 事件都交给 Group 1处理,再也不传递给 View1。而此时 View1 只消费了 DOWN 事件,处于一个事件流的中途阶段,为了让 View1 有一个完整的事件流,就传递给 View1 一个 CANCEL 事件,从而告诉 View1 这个事件流对于它来讲已经结束了。
请求父控件不要拦截事件。属于 ViewParent 的方法,ViewParent是一个接口,ViewGroup 实现了 ViewParent 接口。
模拟条件:
模拟动做: 点击 View1 ;
public class View1 extends View {
public static final String TAG = "----------";
//......代码省略......
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Log.i(TAG, "dispatchTouchEvent--------" + Util.getMotionEvent(event) + this.getClass().getSimpleName());
getParent().requestDisallowInterceptTouchEvent(true);
return super.dispatchTouchEvent(event);
}
}
复制代码
----------: dispatchTouchEvent--------ACTION_DOWN----MainActivity
----------: dispatchTouchEvent--------ACTION_DOWN----Group1
----------: onInterceptTouchEvent--------ACTION_DOWN----Group1
----------: dispatchTouchEvent--------ACTION_DOWN----View1
----------: onTouchEvent--------ACTION_DOWN----View1
----------: dispatchTouchEvent--------ACTION_MOVE----MainActivity
----------: dispatchTouchEvent--------ACTION_MOVE----Group1
----------: dispatchTouchEvent--------ACTION_MOVE----View1
----------: onTouchEvent--------ACTION_MOVE----View1
----------: dispatchTouchEvent--------ACTION_UP------MainActivity
----------: dispatchTouchEvent--------ACTION_UP------Group1
----------: onInterceptTouchEvent--------ACTION_UP------Group1
----------: dispatchTouchEvent--------ACTION_UP------View1
----------: onTouchEvent--------ACTION_UP------View1
复制代码
咱们能够看到 View1 调用 requestDisallowInterceptTouchEvent 后,可以接收到 MOVE 事件以及后续其余事件,Group1 的拦截并无起做用。从而咱们可使用 requestDisallowInterceptTouchEvent 来解决一些开发商的滑动冲突之类的问题。
注意: View 调用 requestDisallowInterceptTouchEvent 请求父控件不拦截生效有意义的前提是: View接收到了 DOWN 事件,而且消费了 DOWN 事件。若是一个控件不消费 DOWN 事件,那么后续事件也不会传递给它。
本篇文章用于记录学习过程当中的理解和笔记,若有错误,请批评指正,万分感谢!
《Android开发艺术探索》第三章