Android中onInterceptTouchEvent与onTouchEvent
onInterceptTouchEvent:
onInterceptTouchEvent 是在ViewGroup里面定义的。Android中的layout布局类一般都是继承此类的。onInterceptTouchEvent是用于拦截手 势事件的,每个手势事件都会先调用onInterceptTouchEvent。
onTouchEvent:
onTouchEvent同样也是在view中定义的一个方法。处理传递到view 的手势事件。手势事件类型包括ACTION_DOWN,ACTION_MOVE,ACTION_UP,ACTION_CANCEL等事件。
其 中Layout里的onInterceptTouchEvent默认返回值是false,这样touch事件会传递到View控件,Layout里的 onTouch默认返回值是false, View里的onTouch默认返回值是true,当我们手指点击屏幕时候,先调用ACTION_DOWN事件,当onTouch里返回值是true的时 候,onTouch回继续调用ACTION_UP事件,如果onTouch里返回值是false,那么onTouch只会调用ACTION_DOWN而不 调用ACTION_UP.
为了让当家更容易理解我写了一个简单的Demo.自定义了Layout与View,Android工程目录如下:
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.FrameLayout;
public class MyLayout extends FrameLayout {
public MyLayout(Context context){
super(context);
}
public MyLayout(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.e(TouchDemoActivity.TAG, "MyLayout onInterceptTouchEvent.");
Log.e(TouchDemoActivity.TAG,"MyLayout onInterceptTouchEvent default return "
+ super.onInterceptTouchEvent(ev));
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e(TouchDemoActivity.TAG, "MyLayout onTouchEvent.");
Log.e(TouchDemoActivity.TAG,"MyLayout onTouchEvent default return "
+ super.onTouchEvent(event));
return super.onTouchEvent(event);
}
}
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.Button;
public class MyView extends Button {
public MyView(Context context){
super(context);
}
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e(TouchDemoActivity.TAG, "MyView onTouchEvent.");
Log.e(TouchDemoActivity.TAG,"MyView onTouchEvent default return "
+ super.onTouchEvent(event));
return super.onTouchEvent(event);
}
}
import android.app.Activity;
import android.os.Bundle;
public class TouchDemoActivity extends Activity {
public static final String TAG = "TouchDemoActivity";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}
<com.tutor.touch.MyLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<com.tutor.touch.MyView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello" />
</com.tutor.touch.MyLayout>
上 面俩个截图都是用系统默认值,可以得出结论:onInterceptTouchEvent默认返回值是false,MyLayout里 onTouchEvent默认返回值是false,所以只消费了ACTION_DOWN事件,MyView里onTouch默认返回值是true,调用了 俩次:ACTION_DOW,ACTION_UP。
下面我们把MyLayout.java里的onInterceptTouchEvent的return值修改为true,代码如下:
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.e(TouchDemoActivity.TAG, "MyLayout onInterceptTouchEvent.");
Log.e(TouchDemoActivity.TAG,"MyLayout onInterceptTouchEvent default return "
+ super.onInterceptTouchEvent(ev));
return true;
}
public boolean onTouchEvent(MotionEvent event) {
Log.e(TouchDemoActivity.TAG, "MyView onTouchEvent.");
Log.e(TouchDemoActivity.TAG,"MyView onTouchEvent default return "
+ super.onTouchEvent(event));
return false;
}
根据上图,我们可以看出MyView里的OnTouchEvent只消费了一次点击事件也就是ACTION_DOWN,还没有执行ACTION_UP,然后跑到MyLayout里又去执行了OnTouchEvent事件。
所以根据上面的内容总结如下:
ViewGroup里的onInterceptTouchEvent默认值是false这样才能把事件传给View里的onTouchEvent.
ViewGroup里的onTouchEvent默认值是false。
View里的onTouchEvent返回默认值是true.这样才能执行多次touch事件。
Android中跟Touch事件有关的事件有三个:
public boolean dispatchTouchEvent(MotionEvent ev):传递Touch事件至target view(可以是自己)。
public boolean onInterceptTouchEvent(MotionEvent ev):在ViewGroup中定义,用于拦截Touch事件的传递。
public boolean onTouchEvent(MotionEvent event): Touch事件处理函数。
先说下事件传递的两种方式:
隧道方式:从根元素依次往下传递直到最内层子元素或在中间某一元素中由于某一条件停止传递。
冒泡方式:从最内层子元素依次往外传递直到根元素或在中间某一元素中由于某一条件停止传递。
Touch事件通过dispatchTouchEvent以隧道方式从上往下传递。如果在一个View中执行onTouchEvent时返回 true的话,接下来的事件(ACTION_DOWN后的ACTION_UP,及可能在中间包含的若干个ACTION_MOVE,从 ACTION_DOWN至ACTION_UP为一个连续事件,这是自己想的,不知道准确否)仍会传递到这个View的onTouchEvent,如果返回 false的话,接下来的事件就不会再传递到这个View,而是执行其Parent View的onTouchEvent,每当一个View的onTouchEvent事件返回false,接下来的事件(如果还有的话)就会止步于这个 View的Parent View,每次上升一个层次,类似于冒泡方式。
Touch事件传递过程中经过的元素都是一个View,但是事件处理的最外层的元素却不是View,除下跟Window有关的事件,当一个 Touch事件发生的时候,会首先调用当前Activity的dispatchTouchEvent函数,然后才将事件传递至下层的View元素。当 dispatchTouchEvent经过一个View往下传递的时候,如果这个View是一个ViewGroup,会调用其 onInterceptTouchEvent函数,这个函数表示是否拦截Touch事件,如果这个函数返回true,表示这个ViewGroup拦截了事 件的传递,Touch事件不会再往下传递给它的子View,而是由它处理,所以会调用它的onTouchEvent函数,如果在传递的过程中没有 ViewGroup拦截事件,即经过的所有ViewGroup都返回false,那么事件最终会传递至最内层的View,一般是一个Widget,当然也 可以是一ViewGroup(其内部不包含任何元素),如果最后事件传递到一View(非ViewGroup),那么会首先调用这个View的 onTouchListener(如果设置了的话),如果onTouchListener返回false则继续调用View的 onTouchEvent(默认返回true),如果最后事件传递到一ViewGroup(无子View),会调用它的onTouchEvent函数,默 认返回false。
如果调用一个View的onTouchEvent函数时返回true的话,那么接下来的Touch Event事件(ACTION_DOWN后的ACTION_UP,及可能在中间包含的若干个ACTION_MOVE,从ACTION_DOWN至 ACTION_UP为一个连续事件,这是自己想的,不知道准确否)仍会传递到这个View并调用它的onTouchEvent函数,在 onTouchEvent函数中可以根据条件返回不同的值,如果某一次在此函数中返回了false那么接下来的Touch Event事件就不会再传递到这个View,而会在其Parent View终止,调用其Parent View的onTouchEvent。如果所有的View都的onTouchEvent函数都返回false,那么接下来的Touch Event事件会由Activity处理,即调用Activity的onTouchEvent函数。
当调用ViewGroup的dispatchTouchEvent函数时,会首先调用onInterceptTouchEvent函数判断有没 有拦截事件,如果没有拦截(返回false),则会依次调用这个ViewGroup的所有子View的dispatchTouchEvent函数。比如一 个FrameLayout上层叠了三个ViewGroup,那么在这个FrameLayout的dispatchTouchEvent中会依次调用这三个 ViewGroup的dispatchTouchEvent函数,而在这三个ViewGroup的dispatchTouchEvent中也会依次调用他 们的子View的dispatchTouchEvent函数,直到其中一个View的dispatchTouchEvent返回true,表示已经处理了 这个Touch事件,不需要再调用这个View的Slibling Views。比如调用这三个层叠的ViewGroup的dispatchTouchEvent函数时,如果第一个ViewGroup的 dispatchTouchEvent函数就返回了true(已经消耗掉了这个事件),那么其他两个ViewGroup的 dispatchTouchEvent就不会再被调用。可以自定义一个ViewGroup的子类并重载他的dispatchTouchEvent函数,使 其处理过Touch事件后仍返回false,那么就还会调用其他兄弟View的dispatchTouchEvent函数。
url:http://greatverve.cnblogs.com/archive/2012/01/12/onInterceptTouchEvent-onTouchEvent.html