一、什么是触摸事件?
触摸事件在Android的API中对应的是MotionEvent类,其中不同的触摸事件对应于不同的事件的类型。
1.1那什么算是触摸事件呢?
Google给出的官方解释是这样的:
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.
大概的意思就是(翻译的不好请多谅解):
用于报告鼠标、笔、手指、轨迹球等移动的对象的事件。运动事件可以保持有绝对或者相对运动以及其他的数据,这要取决于设备的类型。
1.2常用的事件类型
MotionEvent类中有很多事件类型,不同的触摸事件对应于不同的事件类型。常用的主要事件类型如下:
- ACTION_DOWN:用户手指的按下操作,一个按下操作就是一次触摸事件的开始标志;
- ACTION_MOVE:用户手指按压屏幕后,在松开之前,如果移动的距离超过一定的值,就会被判定为ACTION_MOVE操作类型;
- ACTION_UP:用户手指离开屏幕的操作,一次抬起操作标志着一次触摸事件的结束。
每个事件对应都有自己的传递过程,从产生到传递再到停止。并且每个事件的传递载体就是整个View树。
注意:
在一次屏幕触摸事件中,ACTION_DOWN和ACTION_UP是必需的,而ACTION_MOVE则视情况而定,如果用户点击了一下屏幕,是不会触发ACTION_MOVE事件的。
二、事件传递的三个阶段
在Android系统中,拥有时间传递处理能力的类有如下三种:
- Activity及其子类:有dispatchTouchEvent和onTouchEvent两个方法;
- ViewGroup及其子类:有dispatchTouchEvent、onInterceptTouchEvent和onTouchEvent三个方法;
- View及其子类:有dispatchTouchEvent和onTouchEvent两个方法。
时间传递有如下三个阶段:
- 分发(Dispatch):该阶段对应于dispatchTouchEvent方法,在Android系统中,所有的触摸事件都是通过该方法来进行分发的。
- 拦截(Intercept):该阶段对应于onInterceptTouchEvent方法,该方法只在ViewGroup及其子类中才会存在,在View和Activity中是不存在的。
- 消费(Consume):该阶段对应于onTouchEvent方法。
三、探究一下
3.1.xml布局文件
<?xml version="1.0" encoding="utf-8"?>
<com.gimiii.codinglight.MyDemoViewGroup xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
tools:context="com.gimiii.codinglight.MainActivity">
<com.gimiii.codinglight.MyDemoTextView
android:id="@+id/tvAdd"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TextView 1"
android:padding="20dp"
/>
<com.gimiii.codinglight.MyDemoTextView
android:id="@+id/tvQuary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TextView 2"
android:padding="20dp"
/>
</com.gimiii.codinglight.MyDemoViewGroup>
布局文件中有一个自定义的ViewGroup和两个自定义的View。其中MainActivity中包裹着这三个View,ViewGroup中包裹着两个View。
3.2Java代码
- MainActivity中的重写dispatchTouchEvent方法和onTouchEvent方法。
Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.e("log","MainActivity return super.diapstchTouchEvent");
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e("log","MainActivity return super.onTouchEvent");
return super.onTouchEvent(event);
}
- 在MyDemoViewGroup中重写dispatchTouchEvent方法、onInterceptTouchEvent方法和onTouchEvent方法。
public class MyDemoViewGroup extends LinearLayout {
public MyDemoViewGroup(Context context) {
super(context);
}
public MyDemoViewGroup(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public MyDemoViewGroup(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.e("log","MyDemoViewGroup return super.dispatchTouchEvent");
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.e("log","MyDemoViewGroup return super.onInterceptTouchEvent");
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e("log","MyDemoViewGroup return super.onTouchEvent");
return super.onTouchEvent(event);
}
}
- 在MyDemoTextView中重写dispatchTouchEvent方法和onTouchEvent方法。
public class MyDemoTextView extends TextView {
public MyDemoTextView(Context context) {
super(context);
}
public MyDemoTextView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public MyDemoTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Log.e("log","MyDemoTextView return super.ondispatchTouchEvent");
return super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e("log","MyDemoTextView return super.onTouchEvent");
return super.onTouchEvent(event);
}
}
3.3看log日志,有木有新发现
3.3.1 默认情况下,所有的方法都是return super(父类默认实现)。当触摸最里层的MyDemoTextView是,产生ACTION_DOWN事件。
- log日志如下:
- 上图中可以看出,传递流程如下:
3.3.2下面再来探究一下dispatchTouchEvent()方法
(1)将MyDemoTextView中的dispatchTouchEvent()方法设置为return false。
-
log日志如下:
View的dispatchTouchEvent返回false.png
- 上图中可以看出,传递流程如下:
(2)将MyDemoViewGroup中的dispatchTouchEvent()方法设置为return false。
- log日志如下:
- 上图中可以看出,传递流程如下:
(3)将MainActivity中的dispatchTouchEvent()方法设置为return false。
-
log日志如下:
Activity中dispatchTouchEvent返回false.png
对(1)(2)(3)的总结:
当dispatchTouchEvent返回false时,Android会将事件传递给上一级的View的onTouchEvent()方法进行处理。由于Activity没有比它更高级的View,所以如果是Activity的dispatchTouchEvent()方法return false的话,事件会直接被消费掉。
(4)将MyDemoTextView中的dispatchTouchEvent()方法设置为return true。
-
log日志如下:
View中的dispatchTouchEvent返回true.png
- 上图中可以看出,传递流程如下:
(5)将MyDemoViewGroup中的dispatchTouchEvent()方法设置为return true。
-
log日志如下:
ViewGroup中的dispatchTouchEvent返回true.png
-
上图中可以看出,传递流程如下:
ViewGroup中的dispatchTouchEvent设置return true.png
(6)将Activity中的dispatchTouchEvent()方法设置为return true。
-
log日志如下:
Activity中dispatchTouchEvent返回true.png
对(4)(5)(6)的总结:
如果dispatchTouchEvent方法的返回值为true,则事件会被该View消费掉,不再向下传递。
3.3.3下面再来探究一下onTouchEvent方法
(1)将MyDemoTextView中的onTouchEvent()方法设置为return false。
-
log日志如下:
View 中的onTouchEvent返回false.png
-
上图中可以看出,传递流程如下:
View中的onTouchEvent设置return false.png
(2)将MyDemoViewGroup中的onTouchEvent()方法设置为return false。
-
log日志如下:
ViewGroup中的onTouchEvent返回false.png
-
上图中可以看出,传递流程如下:
ViewGroup中的onTouchEvent设置return false.png
(3)将Activity中的onTouchEvent()方法设置为return false。
-
log日志如下:
Activity中onTouchEvent返回false.png
-
上图中可以看出,传递流程如下:
Activity中onTouchEvent设置return false.png
对(1)(2)(3)的总结:
当onTouchEvent方法返回值为false时,跟默认的情况(return super)是一样的,都是传递给上一级的onTouchEvent方法。
(4)将MyDemoTextView中的onTouchEvent()方法设置为return true。
-
log日志如下:
View的onTouchEvent返回true.png
-
上图中可以看出,传递流程如下:
View中的onTouchEvent设置return true.png
(5)将MyDemoViewGroup中的onTouchEvent()方法设置为return true。
-
log日志如下:
ViewGroup的onTouchEvent返回true.png
-
上图中可以看出,传递流程如下:
ViewGroup中的onTouchEvent设置return true.png
(6)将Activity中的onTouchEvent()方法设置为return true。
-
log日志如下:
Activity中onTouchEvent返回true.png
-
上图中可以看出,传递流程如下:
Activity中onTouchEvent设置return true.png
对(4)(5)(6)的总结:
当onTouchEvent方法返回值为true时,事件会被消费掉,不再向下传递。跟dispatchTouchEvent方法返回值为true时类似。
3.3.4最后来探究一下onInterceptTouchEvent方法
(1)将MyDemoViewGroup中的onInterceptTouchEvent()方法设置为return false。
-
log日志如下:
ViewGroup中的onInterceptTouchEvent返回false.png -
上图中可以看出,传递流程如下:
ViewGroup中的onInterceptTouchEvent设置return false.png
总结:
onInterceptTouchEvent()方法return false和return super是一样的。都是将触摸事件传给下一级view的dispatchTouchEvent方法。
(2)将MyDemoViewGroup中的onInterceptTouchEvent()方法设置为return false。
-
log日志如下:
ViewGroup中的onInterceptTouchEvent返回true.png
-
上图中可以看出,传递流程如下:
ViewGroup中的onInterceptTouchEvent设置return true.png
总结:
onInterceptTouchEvent()方法的返回值为true时,Touch事件会被传递给ViewGroup的onTouchEvent()方法进行处理。
4探究总结
-
对于dispatchTouchEvent方法的总结如下表:
dispatchTouchEvent总结.png -
对于onTouchEvent方法的总结如下表:
onTouchEvent总结.png
-
对于onInterceptTouchEvent方法的总结如下表:
onInterceptTouchEvent总结.png -
触摸事件的传递总结:
- 触摸事件的传递顺序是由Activity到ViewGroup,再由ViewGroup递归传递给它的子View;
- ViewGroup通过onInterceptTouchEvent方法对事件进行拦截,如果该方法返回true,泽事件不会继续传递给子View,如果返回false或者super.onInterceptTouchEvent,则事件会继续传递给子View;
- 在子View汇总对事件进行消费后,ViewGroup将接收不到任何事件。
网友评论