Android 开发中,很多情况下,我们需要对触摸事件进行处理,但是当面对错综复杂的 Android 布局时,我们如何准确的将一个用户的触摸事件传递到对应的控件中并让它进行处理呢?
首先,我们先假设这里有这样一个布局:
我们可以很清楚的看到,一个很明显的嵌套布局,外面两个红色的和黄色的都是布局,中间一个紫色的控件。如果此时,我们单击一下 myView 这个控件,触摸事件(单击也是触摸事件)是怎么传递的呢?
Android 中,触摸事件的传递是由外向内的,也就是说,这个触摸事件从 myLinearLayout 开始(由更上面一层的组件将触摸事件传递给 myLinearLayout),依次通过 myFrameLayout,最后传递到 MyView 这个控件中,因为 myView 没有子控件。所以这个事件就由 myView 控件进行处理,然后将处理的结果返回到它的父控件:myFrameLayout ,之后继续返回给 myLinearLayout 控件。。。
当然,我们上面看到的情况是最一般的情况,触摸事件由外向里传递,处理结果由里向外传递。我们也可以通过重写控件或者布局里面的一些方法来拦截触摸事件。
首先,对于 ViewGroup 来说,我们可以选择性的重写下面三个方法:
public boolean dispatchTouchEvent(MotionEvent ev);
public boolean onInterceptTouchEvent(MotionEvent ev);
public boolean onTouchEvent(MotionEvent ev);
我们分别来看一下这三个方法:
这个方法的作用是把触摸事件的分发方法,其返回值代表触摸事件是否被当前 View 处理完成(true/false)。
这个方法是触摸事件拦截机制的核心方法,官方文档解释的很详细,简单来说就是如果这个方法的返回值是 true,那么当前触摸事件就不会传递给子 View 控件(即被当前 ViewGroup 控件拦截并由当前 ViewGroup控件的 onTouchEvent(MotionEvent event) 方法进行处理),如果返回值是 false ,那么这个触摸事件就会传递给子 View 控件,由子 View 控件去处理。
这个是 ViewGroup 控件处理触摸事件的方法,一般来说,ViewGroup 控件的触摸事件在这个方法中处理。如果这个方法返回 true,证明当前触摸事件被当前 ViewGroup 控件处理完成并消耗了,如果返回 false,证明当前触摸事件没有被当前 ViewGroup 控件处理完成。
结合我们上面所讲的,笔者用一张图来表示这三个方法的影响关系(触摸事件由外向里的传递过程。这里是笔者个人的理解):
用伪代码表示 ViewGroup 中三个方法的调用关系:
public boolean dispatchTouchEvent(MotionEvent e) {
bool result = false;
if(interceptTouchEvent(e)) {
result = onTouchEvent(e);
} else {
result = child.dispatchTouchEvent(e);
}
return result;
}
上面的三个方法是 ViewGroup 对象中拥有的,而对于 View 对象来说,只有下面两个方法:
public boolean dispatchTouchEvent(MotionEvent ev);
public boolean onTouchEvent(MotionEvent ev);
View 对象没有 onInterceptTouchEvent 方法,即没有拦截事件的方法(因为 View 对象已经是最内层 View 控件,它没有子 View 了),所以不存在拦截事件这个说法,如果触摸事件传递到最内层 View 控件,那么这个 View 控件的 onTouchEvent 方法一定会被调用用于处理触摸事件。其伪代码如下:
/**
* dispatchTouchEvent(MotionEvent ev) 方法的返回值代表这个 View 是否成功处理触摸事件
*/
public boolean dispatchTouchEvent(MotionEvent ev) {
return onTouchEvent(ev);
}
接下来要明白:
1、无论是对于 View 还