天天看点

onTouchEvent 和 onInterceptTouchEvent 事件触摸

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)先点击界面中的按钮

onTouchEvent 和 onInterceptTouchEvent 事件触摸

(2)再点击界面中的其它区域

onTouchEvent 和 onInterceptTouchEvent 事件触摸

结论:LLayout 中 onInterceptTouchEvent 默认返回值为false,onTouchEvent 默认返回值为false,所以只调用了ACTION_DOWN事件;

LView中 onTouchEvent 默认返回值为true;调用了ACTION_DOWN,ACTION_UP 两个事件;

(3)修改LLayout中onInterceptTouchEvent返回值为true,再次运行代码:

onTouchEvent 和 onInterceptTouchEvent 事件触摸

结论:LLayout中onInterceptTouchEvent返回了true,对触摸事件进行了拦截,所以没有将事件传递给View,而直接执行了LLayout中的onTouchEvent事件;

(4)把LLayout中onInterceptTouchEvent返回值改为false,再把LView中的onTouchEvent改为返回false:

onTouchEvent 和 onInterceptTouchEvent 事件触摸

结论:由于将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。
            ...
        }  
    }  
	...
}
           

继续阅读