Android onTouch()和onTouchEvent()区别
1、onTouch()方法:
onTouch方式是View的OnTouchListener接口中定义的方法。
当一个View绑定了OnTouchListener后,当有Touch事件触发时,就会调用onTouch方法。
(当把手放到View上后,onTouch方法被一遍一遍的调用)
2、onTouchEvent()方法:
onTouchEvent方法时重载的Activity的方法
重写了Acitivity的onTouchEvent方法后,当屏幕有Touch事件时,此方法就会被调用。
(当把手放到Activity上时,onTouchEvent方法会一遍一遍的被调用)
3、Touch事件的传递:
在一个Activity里面放一个TextView的实例tv,并且这个tv的属性设定为march_parent
在这种情况下,当手放到屏幕上的时候,首先会是tv响应Touch事件,执行onTouch方法。
如果onTouch返回值为true,表示这个Touch事件被onTouch方法处理完毕,不会把Touch事件再传递给Activity
也就是说onTouchEvent方法不会被调用
(手放到屏幕上后,onTouch方法会被一遍一遍的调用)
如果onTouch返回值为false,表示这个Touch事件没有被tv完全处理,onTouch返回以后,Touch事件被传递给Activity,
onTouchEvent方法调用
(当把手放到屏幕上后,onTouch方法调用一次后,onTouchEvent方法被一遍一遍的调用)
测试:
1、MyLinearLayout继承LinearLayout,并重写onTouchEvent方法
1 import android.content.Context;
2 import android.util.AttributeSet;
3 import android.util.Log;
4 import android.view.MotionEvent;
5 import android.widget.LinearLayout;
6
7 public class MyLinearLayout extends LinearLayout{
8
9 public MyLinearLayout(Context context, AttributeSet attrs) {
10 super(context, attrs);
11 }
12
13 @Override
14 public boolean onTouchEvent(MotionEvent event) {
15 switch (event.getAction()) {
16 case MotionEvent.ACTION_DOWN://0
17 Log.e("TAG", "LinearLayout onTouchEvent 按住");
18 break;
19 case MotionEvent.ACTION_UP://1
20 Log.e("TAG", "LinearLayout onTouchEvent onTouch抬起");
21 break;
22 case MotionEvent.ACTION_MOVE://2
23 Log.e("TAG", "LinearLayout onTouchEvent 移动");
24 break;
25 }
26 return super.onTouchEvent(event);
27 }
28
29 @Override
30 public boolean dispatchTouchEvent(MotionEvent ev) {
31 return super.dispatchTouchEvent(ev);
32 }
33
34 @Override
35 public void setOnTouchListener(OnTouchListener l) {
36 super.setOnTouchListener(l);
37 }
38
39 }
2、在MainActivity中声明MyLinearLayout,并设置触摸监听和点击监听,同时MainActivity自己也有onTouchEvent方法,重写看结果
1 import android.app.Activity;
2 import android.os.Bundle;
3 import android.util.Log;
4 import android.view.MotionEvent;
5 import android.view.View;
6 import android.view.View.OnClickListener;
7 import android.view.View.OnTouchListener;
8
9 public class MainActivity extends Activity {
10
11 MyLinearLayout ll;
12 @Override
13 protected void onCreate(Bundle savedInstanceState) {
14 super.onCreate(savedInstanceState);
15 setContentView(R.layout.activity_main);
16
17 ll = (MyLinearLayout) findViewById(R.id.ll);
18 //触摸监听
19 ll.setOnTouchListener(new OnTouchListener() {
20
21 @Override
22 public boolean onTouch(View v, MotionEvent event) {
23 //Log.e("TAG", event.getX()+" "+event.getY());
24
25 switch (event.getAction()) {
26 case MotionEvent.ACTION_DOWN://0
27 Log.e("TAG", "LinearLayout onTouch按住");
28 break;
29 case MotionEvent.ACTION_UP://1
30 Log.e("TAG", "LinearLayout onTouch抬起");
31 break;
32 case MotionEvent.ACTION_MOVE://2
33 Log.e("TAG", "LinearLayout onTouch移动");
34 break;
35 }
36 //事件分发
37 //1、setOnTouchListener单独使用的时候返回值需要true,这样才能保证移动的时候获取相应的监听,而非一次监听(即只有按下事件)
38 //返回false,表示没有被处理,将向父View传递。只能监听到view的"按下"事件,"移动"和"抬起"都不能监听到。因为down事件未结束
39 //返回true,消耗此事件,表示正确接收并处理,不在分发。"按下""抬起""移动"都能监听到了
40
41 //2、setOnTouchListener和setOnClickListener同时使用时,
42 //返回true,事件被onTouch消耗掉了,因而不会在继续向下传递。只能监听"按下""抬起""移动",不能监听到"点击";
43 //返回false,"按下""抬起""移动""点击"都能监听
44
45 return false;
46 /**
47 * onTouch是优先于onClick的,并且执行了两次,一次是ACTION_DOWN,一次是ACTION_UP(可能还会有多次ACTION_MOVE),
48 * 因此事件传递的顺序是先经过OnTouch,再传递给onClick
49 *
50 */
51 }
52 });
53
54 ll.setOnClickListener(new OnClickListener() {
55 @Override
56 public void onClick(View v) {
57 Log.e("TAG", "onClick点击事件");
58 }
59 });
60 }
61
62 //事件分发:public boolean dispatchTouchEvent(MotionEvent ev)
63 //当监听到事件时,首先由Activity的捕获到,进入事件分发处理流程,无论是Activity还是View,事件分发自身也具有消费能力
64 //如果事件分发返回true,表示该事件在本层不再进行分发且已经已经在事件分发自身中被消费了。
65 //如果你不想Activity中的任何控件具有任何的事件消费能力,可以直接重写Activity的dispatchTouchEvent方法,返回true就可以了
66 @Override
67 public boolean dispatchTouchEvent(MotionEvent ev) {
68 // TODO Auto-generated method stub
69 return super.dispatchTouchEvent(ev);
70 }
71
72
73 @Override
74 public boolean onTouchEvent(MotionEvent event) {
75 switch (event.getAction()) {
76 case MotionEvent.ACTION_DOWN://0
77 Log.e("TAG", "Activity onTouchEvent按住");
78 break;
79 case MotionEvent.ACTION_UP://1
80 Log.e("TAG", "Activity onTouchEvent抬起");
81 break;
82 case MotionEvent.ACTION_MOVE://2
83 Log.e("TAG", "Activity onTouchEvent移动");
84 break;
85 }
86 return super.onTouchEvent(event);
87 }
88
89
90 }
3、由于MyLinearLayout继承了LinearLayou,但是XML并不认识他,所以在xml中搭建布局的时候,一定要包名带类名
1 <com.example.lesson6_ontouch.MyLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
2 android:id="@+id/ll"
3 android:layout_width="match_parent"
4 android:layout_height="match_parent">
5
6 <TextView
7 android:id="@+id/tv"
8 android:layout_width="match_parent"
9 android:layout_height="match_parent"
10 android:text="hsss"/>
11
12 </com.example.lesson6_ontouch.MyLinearLayout>
测试结果:

4、下面贴一位博主的分析 Android事件分发机制完全解析,带你从源码的角度彻底理解(上)
首先你需要知道一点,只要你触摸到了任何一个控件,就一定会调用该控件的dispatchTouchEvent方法。那当我们去点击按钮的时候,就会去调用Button类里的dispatchTouchEvent方法,可是你会发现Button类里并没有这个方法,那么就到它的父类TextView里去找一找,你会发现TextView里也没有这个方法,那没办法了,只好继续在TextView的父类View里找一找,这个时候你终于在View里找到了这个方法,示意图如下:
然后我们来看一下View中dispatchTouchEvent方法的源码:
1 public boolean dispatchTouchEvent(MotionEvent event) {
2 if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
3 mOnTouchListener.onTouch(this, event)) {
4 return true;
5 }
6 return onTouchEvent(event);
7 }
我们可以看到,在这个方法内,首先是进行了一个判断,如果mOnTouchListener != null,(mViewFlags & ENABLED_MASK) == ENABLED和mOnTouchListener.onTouch(this, event)这三个条件都为真,就返回true,否则就去执行onTouchEvent(event)方法并返回。
先看一下第一个条件,mOnTouchListener这个变量是在哪里赋值的呢?我们寻找之后在View里发现了如下方法:
|
1
2
3
|
public void setOnTouchListener(OnTouchListener l) { mOnTouchListener = l; } |
mOnTouchListener正是在setOnTouchListener方法里赋值的,也就是说只要我们给控件注册了touch事件,mOnTouchListener就一定被赋值了。
第二个条件(mViewFlags & ENABLED_MASK) == ENABLED是判断当前点击的控件是否是enable的,按钮默认都是enable的,因此这个条件恒定为true。
第三个条件就比较关键了,mOnTouchListener.onTouch(this, event),其实也就是去回调控件注册touch事件时的onTouch方法。也就是说如果我们在onTouch方法里返回true,就会让这三个条件全部成立,从而整个方法直接返回true。如果我们在onTouch方法里返回false,就会再去执行onTouchEvent(event)方法。
现在我们可以结合前面的例子来分析一下了,首先在dispatchTouchEvent中最先执行的就是onTouch方法,因此onTouch肯定是要优先于onClick执行的,也是印证了刚刚的打印结果。而如果在onTouch方法里返回了true,就会让dispatchTouchEvent方法直接返回true,不会再继续往下执行。而打印结果也证实了如果onTouch返回true,onClick就不会再执行了。
根据以上源码的分析,从原理上解释了我们前面例子的运行结果。而上面的分析还透漏出了一个重要的信息,那就是onClick的调用肯定是在onTouchEvent(event)方法中的!那我们马上来看下onTouchEvent的源码,如下所示:
1 public boolean onTouchEvent(MotionEvent event) {
2 final float x = event.getX();
3 final float y = event.getY();
4 final int viewFlags = mViewFlags;
5 final int action = event.getAction();
6
7 if ((viewFlags & ENABLED_MASK) == DISABLED) {
8 if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
9 setPressed(false);
10 }
11 // A disabled view that is clickable still consumes the touch
12 // events, it just doesn't respond to them.
13 return (((viewFlags & CLICKABLE) == CLICKABLE
14 || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
15 || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE);
16 }
17 if (mTouchDelegate != null) {
18 if (mTouchDelegate.onTouchEvent(event)) {
19 return true;
20 }
21 }
22
23 if (((viewFlags & CLICKABLE) == CLICKABLE ||
24 (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||
25 (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) {
26 switch (action) {
27 case MotionEvent.ACTION_UP:
28 boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
29 if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
30 // take focus if we don't have it already and we should in
31 // touch mode.
32 boolean focusTaken = false;
33 if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
34 focusTaken = requestFocus();
35 }
36
37 if (prepressed) {
38 // The button is being released before we actually
39 // showed it as pressed. Make it show the pressed
40 // state now (before scheduling the click) to ensure
41 // the user sees it.
42 setPressed(true, x, y);
43 }
44
45 if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
46 // This is a tap, so remove the longpress check
47 removeLongPressCallback();
48
49 // Only perform take click actions if we were in the pressed state
50 if (!focusTaken) {
51 // Use a Runnable and post this rather than calling
52 // performClick directly. This lets other visual state
53 // of the view update before click actions start.
54 if (mPerformClick == null) {
55 mPerformClick = new PerformClick();
56 }
57 if (!post(mPerformClick)) {
58 performClick();
59 }
60 }
61 }
62
63 if (mUnsetPressedState == null) {
64 mUnsetPressedState = new UnsetPressedState();
65 }
66
67 if (prepressed) {
68 postDelayed(mUnsetPressedState,
69 ViewConfiguration.getPressedStateDuration());
70 } else if (!post(mUnsetPressedState)) {
71 // If the post failed, unpress right now
72 mUnsetPressedState.run();
73 }
74
75 removeTapCallback();
76 }
77 mIgnoreNextUpEvent = false;
78 break;
79
80 case MotionEvent.ACTION_DOWN:
81 mHasPerformedLongPress = false;
82
83 if (performButtonActionOnTouchDown(event)) {
84 break;
85 }
86
87 // Walk up the hierarchy to determine if we're inside a scrolling container.
88 boolean isInScrollingContainer = isInScrollingContainer();
89
90 // For views inside a scrolling container, delay the pressed feedback for
91 // a short period in case this is a scroll.
92 if (isInScrollingContainer) {
93 mPrivateFlags |= PFLAG_PREPRESSED;
94 if (mPendingCheckForTap == null) {
95 mPendingCheckForTap =

