http://www.android100.org/html/201502/26/123262.html
首先从字面意思理解两个词
onTouchEvent:触发触摸事件
onInterceptTouchEvent:触发拦截触摸事件
通过查看源代码及类继承关系
onInterceptTouchEvent:是定义于ViewGroup里面的一个方法,此事件是用于拦截触摸事件的,ViewGroup(继承自View),一个View的Group,也就是我们的一个布局如LinerLayout,各个布局类都继承自ViewGroup;
onTouchEvent:是定义于View中的一个方法,处理传递到View的手势触摸事件。手势事件类型包括ACTION_DOWN,ACTION_MOVE,ACTION_UP,ACTION_CANCEL等事件;
其中ViewGroup里的onInterceptTouchEvent默认返回值是false,这样touch事件会传递到View控件,ViewGroup里的onTouchEvent默认返回值是false;
View里的onTouchEvent默认返回值是true,当我们手指点击屏幕时候,先调用ACTION_DOWN事件,当onTouchEvent里返回值是true的时候,onTouch会继续调用ACTION_UP事件,如果onTouchEvent里返回值是false,那么onTouchEvent只会调用ACTION_DOWN而不调用ACTION_UP。
1、新建两个类LLayout , LView 如下
public class LLayout extends FrameLayout {
// ViewGroup
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.i("LTAG", "LLayout onInterceptTouchEvent");
Log.i("LTAG", "LLayout onInterceptTouchEvent default return" + super.onInterceptTouchEvent(ev));
return super.onInterceptTouchEvent(ev);
}
// View
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.i("LTAG", "LLayout onTouchEvent");
Log.i("LTAG", "LLayout onTouchEvent default return" + super.onTouchEvent(event));
return super.onTouchEvent(event);
}
}
public class LView extends Button {
// TextView <-- View
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.i("LTAG", "onTouchEvent");
Log.i("LTAG", "onTouchEvent default return" + super.onTouchEvent(event));
return super.onTouchEvent(event);
}
}
2、修改布局文件为如下布局
<com.touchpro.LLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<com.touchpro.LView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/app_name" />
</com.touchpro.LLayout>
(1)先点击界面中的按钮
(2)再点击界面中的其它区域
结论:LLayout 中 onInterceptTouchEvent 默认返回值为false,onTouchEvent 默认返回值为false,所以只调用了ACTION_DOWN事件;
LView中 onTouchEvent 默认返回值为true;调用了ACTION_DOWN,ACTION_UP 两个事件;
(3)修改LLayout中onInterceptTouchEvent返回值为true,再次运行代码:
结论:LLayout中onInterceptTouchEvent返回了true,对触摸事件进行了拦截,所以没有将事件传递给View,而直接执行了LLayout中的onTouchEvent事件;
(4)把LLayout中onInterceptTouchEvent返回值改为false,再把LView中的onTouchEvent改为返回false:
结论:由于将LView中onTouchEvent返回值修改为false,因此只执行了ACTION_DOWN,然后就到LLayout中执行onTouchEvent事件了;
ViewGroup里的onInterceptTouchEvent默认值是false这样才能把事件传给View里的onTouchEvent.
ViewGroup里的onTouchEvent默认值是false。
View里的onTouchEvent返回默认值是true.这样才能执行多次touch事件。
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
View事件详解:
onTouch:一般需要调用setOnTouchListener方法,即可看到该方法。
默认返回false,如果返回true事件不在传送。对于view来说先调用ontouch在调用onClick(如果onTouch返回true,则onClick方法不执行)
onClick:点击事件
dispatchTouchEvent事件:当触摸任何一个控件,就一定会调用该控件的dispatchTouchEvent方法,
public boolean dispatchTouchEvent(MotionEvent event) {
if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
mOnTouchListener.onTouch(this, event)) {
return true;
}
return onTouchEvent(event);
}
如果调用了setOnTouchListener方法第一个条件为true,只要按钮没有被禁用第二个条件为true,第三个条件就比较关键了,mOnTouchListener.onTouch(this, event),其实也就是去回调控件注册touch事件时的onTouch方法。也就是说如果
我们在onTouch方法里返回true,就会让这三个条件全部成立,从而整个方法直接返回true。如果我们在onTouch方法里返回false,就会再去执行onTouchEvent(event)方法。
由上面可知,onClick的调用肯定是在onTouchEvent(event)方法中的。
我们都知道如果给一个控件注册了touch事件,每次点击它的时候都会触发一系列的ACTION_DOWN,ACTION_MOVE,ACTION_UP等事件。这里需要注意,如果你在执行ACTION_DOWN的时候返回了false,后面一系列其它的action就不会再得到执行了。
简单的说,就是当dispatchTouchEvent在进行事件分发的时候,只有前一个action返回true,才会触发后一个action。
public boolean onTouchEvent(MotionEvent event) {
final int viewFlags = mViewFlags;
if (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) { //只要按钮可以点击,就返回true
switch (event.getAction()) {
case MotionEvent.ACTION_UP:
...
performClick();
...
break;
}
return true;
}
return false;
}
ViewGroup 事件传递:
ViewGroup中的dispatchTouchEvent方法的源码
public boolean onInterceptTouchEvent(MotionEvent ev) {
return false;
}
public boolean dispatchTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
final float xf = ev.getX();
final float yf = ev.getY();
final float scrolledXFloat = xf + mScrollX;
final float scrolledYFloat = yf + mScrollY;
final Rect frame = mTempRect;
boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (action == MotionEvent.ACTION_DOWN) {
if (mMotionTarget != null) {
mMotionTarget = null;
}
if (disallowIntercept || !onInterceptTouchEvent(ev)) { //调用了onInterceptTouchEvent方法,它默认返回false,因此view控件的onClick才能执行。如果当前viewgroup需要处理事件,可以将其返回true。
...
}
}
...
}