android事件学习

一、android处理事件有两种形式.

  1、基于监听的事件处理,就是通过setXxxListenter()进行处理的。

  2、基于回调的事件处理,就是View类内部的onTouchEvent(),一般是在自定义控件时重写的。

关于这些方法是在什么时候被触发的,下面是对部分源码的分析:

  1、首先:触摸事件会触发Activity的dispatchTouchEvent,

 1 public boolean dispatchTouchEvent(MotionEvent ev) {
 2     // onUserInteraction默认不执行任何动作。
 3     // 它是提供给使用者的接口。
 4     if (ev.getAction() == MotionEvent.ACTION_DOWN) {
 5         onUserInteraction();
 6     }
 7     // 这里会调用到ViewGroup的dispatchTouchEvent(),
 8     // 即会调用Activity包含的根视图的dispatchTouchEvent()。
 9     if (getWindow().superDispatchTouchEvent(ev)) {
10         return true;
11     }
12     // 如果superDispatchTouchEvent()返回false,
13     // 即Activity的根视图以及根视图的子视图都没有拦截该事件的话,则调用Activity的onTouchEvent()
14     return onTouchEvent(ev);
15 }
Activity的dispatchTouchEvent

    对于getWindow().superDispatchTouchEvent(ev)一句追踪源码可以发现()这里调用了DecorView的superDispatchTouchEvent(),而DecorView是PhoneWindow的内部类,并且是Window界面的顶级View

1 private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {
2     ...
3 
4     public boolean superDispatchTouchEvent(MotionEvent event) {
5         return super.dispatchTouchEvent(event);
6     }
7 
8     ...
9 }
DecorView

    从上面的DecorView源码部分可以看到:通过DecorView,Activity的触摸事件将转发给GroupView的dispatchTouchEvent()进行处理。

    在Activity的dispatchTouchEvent()中,如果GroupView的dispatchTouchEvent()返回true则会拦截消耗事件,否则将执行Activity的onTouchEvent。

 1 public boolean onTouchEvent(MotionEvent event) {
 2     if (mWindow.shouldCloseOnTouch(this, event)) {
 3         finish();
 4         return true;
 5     }    
 6 
 7     return false;
 8 }  
 9 
10 //上面会调用Window的shouldCloseOnTouch()
11 public boolean shouldCloseOnTouch(Context context, MotionEvent event) {
12     if (mCloseOnTouchOutside && event.getAction() == MotionEvent.ACTION_DOWN
13             && isOutOfBounds(context, event) && peekDecorView() != null) {
14         return true;
15     }    
16     return false;
17 }   
onTouchEvent

    上面可以看到:这个方法是对ctivity自身触摸事件的默认处理,可以用于关闭Dialog主题的Activity

    !!!详询参见http://wangkuiwu.github.io/2015/01/02/TouchEvent-Activity/

   2、接着:由GroupView的dispatchTouchEvent()继续进行事件的分发

  1 public boolean dispatchTouchEvent(MotionEvent ev) {
  2     // mInputEventConsistencyVerifier是调试用的,不会理会
  3     if (mInputEventConsistencyVerifier != null) {
  4         mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
  5     }
  6 
  7     // 第1步:是否要分发该触摸事件
  8     //
  9     // onFilterTouchEventForSecurity()表示是否要分发该触摸事件。 
 10     // 如果该View不是位于顶部,并且有设置属性使该View不在顶部时不响应触摸事件,则不分发该触摸事件,即返回false。
 11     // 否则,则对触摸事件进行分发,即返回true。
 12     boolean handled = false;
 13     if (onFilterTouchEventForSecurity(ev)) {
 14         final int action = ev.getAction();
 15         final int actionMasked = action & MotionEvent.ACTION_MASK;
 16 
 17         // 第2步:检测是否需要清空目标和状态
 18         //
 19         // 如果是ACTION_DOWN(即按下事件),则清空之前的触摸事件处理目标和状态。
 20         // 这里的情况状态包括:
 21         // (01) 清空mFirstTouchTarget链表,并设置mFirstTouchTarget为null。
 22         //      mFirstTouchTarget是"接受触摸事件的View"所组成的单链表
 23         // (02) 清空mGroupFlags的FLAG_DISALLOW_INTERCEPT标记
 24         //      如果设置了FLAG_DISALLOW_INTERCEPT,则不允许ViewGroup对触摸事件进行拦截。
 25         // (03) 清空mPrivateFlags的PFLAG_CANCEL_NEXT_UP_EVEN标记
 26         if (actionMasked == MotionEvent.ACTION_DOWN) {
 27             cancelAndClearTouchTargets(ev);
 28             resetTouchState();
 29         }    
 30 
 31         // 第3步:检查当前ViewGroup是否想要拦截触摸事件
 32         // 
 33         // 是的话,设置intercepted为true;否则intercepted为false。
 34         // 如果是"按下事件(ACTION_DOWN)" 或者 mFirstTouchTarget不为null;就执行if代码块里面的内容。
 35         // 否则的话,设置intercepted为true。
 36         final boolean intercepted;
 37         if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) {
 38             // 检查禁止拦截标记:FLAG_DISALLOW_INTERCEPT
 39             // 如果调用了requestDisallowInterceptTouchEvent()标记的话,则FLAG_DISALLOW_INTERCEPT会为true。
 40             // 例如,ViewPager在处理触摸事件的时候,就会调用requestDisallowInterceptTouchEvent()
 41             //     ,禁止它的父类对触摸事件进行拦截
 42             final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
 43             if (!disallowIntercept) {
 44                 // 如果禁止拦截标记为false的话,则调用onInterceptTouchEvent();并返回拦截状态。
 45                 intercepted = onInterceptTouchEvent(ev);
 46                 ev.setAction(action); // restore action in case it was changed
 47             } else {
 48                 intercepted = false;
 49             }    
 50         } else {
 51             intercepted = true;
 52         }    
 53 
 54         // 第4步:检查当前的触摸事件是否被取消
 55         // 
 56         // (01) 对于ACTION_DOWN而言,mPrivateFlags的PFLAG_CANCEL_NEXT_UP_EVENT位肯定是0;因此,canceled=false。
 57         // (02) 当前的View或ViewGroup要被从父View中detach时,PFLAG_CANCEL_NEXT_UP_EVENT就会被设为true;
 58         //      此时,它就不再接受触摸事情。
 59         final boolean canceled = resetCancelNextUpFlag(this)
 60                 || actionMasked == MotionEvent.ACTION_CANCEL;
 61 
 62         // 第5步:将触摸事件分发给"当前ViewGroup的子View和子ViewGroup"
 63         // 
 64         // 如果触摸"没有被取消",同时也"没有被拦截"的话,则将触摸事件分发给它的子View和子ViewGroup。  
 65         //     如果当前ViewGroup的孩子有接受触摸事件的话,则将该孩子添加到mFirstTouchTarget链表中。
 66         final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
 67         TouchTarget newTouchTarget = null;
 68         boolean alreadyDispatchedToNewTouchTarget = false;
 69         if (!canceled && !intercepted) {
 70             if (actionMasked == MotionEvent.ACTION_DOWN
 71                     || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
 72                     || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
 73                 // 这是获取触摸事件的序号 以及 触摸事件的id信息。
 74                 // (01) 对于ACTION_DOWN,actionIndex肯定是0
 75                 // (02) 而getPointerId()是获取的该触摸事件的id,并将该id信息保存到idBitsToAssign中。
 76                 //    这个触摸事件的id是为多指触摸而添加的;对于单指触摸,getActionIndex()返回的肯定是0;
 77                 //    而对于多指触摸,第一个手指的id是0,第二个手指的id是1,第三个手指的id是2,...依次类推。
 78                 final int actionIndex = ev.getActionIndex();
 79                 final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
 80                         : TouchTarget.ALL_POINTER_IDS;
 81 
 82                 // 清空这个手指之前的TouchTarget链表。
 83                 // 一个TouchTarget,相当于一个可以被触摸的对象;它中记录了接受触摸事件的View
 84                 removePointersFromTouchTargets(idBitsToAssign);
 85 
 86                 // 获取该ViewGroup包含的View和ViewGroup的数目,
 87                 // 然后递归遍历ViewGroup的孩子,对触摸事件进行分发。
 88                 // 递归遍历ViewGroup的孩子:是指对于当前ViewGroup的所有孩子,都会逐个遍历,并分发触摸事件;
 89                 //   对于逐个遍历到的每一个孩子,若该孩子是ViewGroup类型的话,则会递归到调用该孩子的孩子,...
 90                 final int childrenCount = mChildrenCount;
 91                 if (newTouchTarget == null && childrenCount != 0) {
 92                     final float x = ev.getX(actionIndex);
 93                     final float y = ev.getY(actionIndex);
 94                     final View[] children = mChildren;
 95 
 96                     final boolean customOrder = isChildrenDrawingOrderEnabled();
 97                     for (int i = childrenCount - 1; i >= 0; i--) {
 98                         final int childIndex = customOrder ?
 99                                 getChildDrawingOrder(childrenCount, i) : i;
100                         final View child = children[childIndex];
101                         // 如果child可以接受触摸事件,
102                         // 并且触摸坐标(x,y)在child的可视范围之内的话;
103                         // 则继续往下执行。否则,调用continue。
104                         // child可接受触摸事件:是指child的是可见的(VISIBLE);或者虽然不可见,但是位于动画状态。
105                         if (!canViewReceivePointerEvents(child)
106                                 || !isTransformedTouchPointInView(x, y, child, null)) {
107                             continue;
108                         }
109 
110                         // getTouchTarget()的作用是查找child是否存在于mFirstTouchTarget的单链表中。
111                         // 是的话,返回对应的TouchTarget对象;否则,返回null。
112                         newTouchTarget = getTouchTarget(child);
113                         if (newTouchTarget != null) {
114                             newTouchTarget.pointerIdBits |= idBitsToAssign;
115                             break;
116                         }
117 
118                         // 重置child的mPrivateFlags变量中的PFLAG_CANCEL_NEXT_UP_EVENT位。
119                         resetCancelNextUpFlag(child);
120 
121                         // 调用dispatchTransformedTouchEvent()将触摸事件分发给child。
122                         if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
123                             // 如果child能够接受该触摸事件,即child消费或者拦截了该触摸事件的话;
124                             // 则调用addTouchTarget()将child添加到mFirstTouchTarget链表的表头,并返回表头对应的TouchTarget
125                             // 同时还设置alreadyDispatchedToNewTouchTarget为true。
126                             mLastTouchDownTime = ev.getDownTime();
127                             mLastTouchDownIndex = childIndex;
128                             mLastTouchDownX = ev.getX();
129                             mLastTouchDownY = ev.getY();
130                             newTouchTarget = addTouchTarget(child, idBitsToAssign);
131                             alreadyDispatchedToNewTouchTarget = true;
132                             break;
133                         }
134                     }
135                 }
136 
137                 // 如果newTouchTarget为null,并且mFirstTouchTarget不为null;
138                 // 则设置newTouchTarget为mFirstTouchTarget链表中第一个不为空的节点。
139                 if (newTouchTarget == null && mFirstTouchTarget != null) {
140                     // Did not find a child to receive the event.
141                     // Assign the pointer to the least recently added target.
142                     newTouchTarget = mFirstTouchTarget;
143                     while (newTouchTarget.next != null) {
144                         newTouchTarget = newTouchTarget.next;
145                     }
146                     newTouchTarget.pointerIdBits |= idBitsToAssign;
147                 }
148             }
149         }
150 
151         // 第6步:进一步的对触摸事件进行分发
152         // 
153         // (01) 如果mFirstTouchTarget为null,意味着还没有任何View来接受该触摸事件;
154         //   此时,将当前ViewGroup看作一个View;
155         //   将会调用"当前的ViewGroup的父类View的dispatchTouchEvent()"对触摸事件进行分发处理。
156         //   即,会将触摸事件交给当前ViewGroup的onTouch(), onTouchEvent()进行处理。
157         // (02) 如果mFirstTouchTarget不为null,意味着有ViewGroup的子View或子ViewGroup中,
158         //   有可以接受触摸事件的。那么,就将触摸事件分发给这些可以接受触摸事件的子View或子ViewGroup。
159         if (mFirstTouchTarget == null) {
160             // 注意:这里的第3个参数是null
161             handled = dispatchTransformedTouchEvent(ev, canceled, null,
162                     TouchTarget.ALL_POINTER_IDS);
163         } else {
164             // Dispatch to touch targets, excluding the new touch target if we already
165             // dispatched to it.  Cancel touch targets if necessary.
166             TouchTarget predecessor = null;
167             TouchTarget target = mFirstTouchTarget;
168             while (target != null) {
169                 final TouchTarget next = target.next;
170                 if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
171                     handled = true;
172                 } else {
173                     final boolean cancelChild = resetCancelNextUpFlag(target.child)
174                             || intercepted;
175                     if (dispatchTransformedTouchEvent(ev, cancelChild,
176                             target.child, target.pointerIdBits)) {
177                         handled = true;
178                     }
179                     if (cancelChild) {
180                         if (predecessor == null) {
181                             mFirstTouchTarget = next;
182                         } else {
183                             predecessor.next = next;
184                         }
185                         target.recycle();
186                         target = next;
187                         continue;
188                     }
189                 }
190                 predecessor = target;
191                 target = next;
192             }
193         }
194 
195         // 第7步:再次检查取消标记,并进行相应的处理
196         // 
197         // Update list of touch targets for pointer up or cancel, if needed.
198         if (canceled
199                 || actionMasked == MotionEvent.ACTION_UP
200                 || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
201             resetTouchState();
202         } else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {
203             final int actionIndex = ev.getActionIndex();
204             final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);
205             removePointersFromTouchTargets(idBitsToRemove);
206         }
207     }
208 
209     // mInputEventConsistencyVerifier是调试用的,不会理会
210     if (!handled && mInputEventConsistencyVerifier != null) {
211         mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);
212     }
213     return handled;
214 }
GroupView的dispatchTouchEvent

    上面还需注意的就是第5步:在分发事件时,除了判断是否被拦截或取消后,内部又判断了是否是Down事件,也就是只有在Down事件没有被拦截或取消,才会向其分发事件

参考:http://wangkuiwu.github.io/2015/01/04/TouchEvent-ViewGroup/

    3、最后,触摸事件会触发view中的dispatchTouchEvent(),如果没有子类中没有重写,则会向上寻找一个执行。下面是View中的部分源码,就是具体执行事件的部分

1  ListenerInfo li = mListenerInfo;
2 if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED&& li.mOnTouchListener.onTouch(this, event)) {
3           result = true;
4  }
5 
6  if (!result && onTouchEvent(event)) {
7          result = true;
8  } 
顶级View的dispatchTouchEvent

  上面是23版本的片段,这部分代码写的比较巧妙,第一个if中:

    1、li != null,这是ListenerInfo对象,保存各种监听器实例的对象,只要有一个监听器被设置,则该对象不为空。

 1     static class ListenerInfo {
 2         protected OnFocusChangeListener mOnFocusChangeListener;
 3 
 4         private ArrayList<OnLayoutChangeListener> mOnLayoutChangeListeners;
 5 
 6         protected OnScrollChangeListener mOnScrollChangeListener;
 7 
 8         private CopyOnWriteArrayList<OnAttachStateChangeListener> mOnAttachStateChangeListeners;
 9 
10         public OnClickListener mOnClickListener;
11 
12         protected OnLongClickListener mOnLongClickListener;
13 
14         protected OnContextClickListener mOnContextClickListener;
15 
16         protected OnCreateContextMenuListener mOnCreateContextMenuListener;
17 
18         private OnKeyListener mOnKeyListener;
19 
20         private OnTouchListener mOnTouchListener;
21 
22         private OnHoverListener mOnHoverListener;
23 
24         private OnGenericMotionListener mOnGenericMotionListener;
25 
26         private OnDragListener mOnDragListener;
27 
28         private OnSystemUiVisibilityChangeListener mOnSystemUiVisibilityChangeListener;
29 
30         OnApplyWindowInsetsListener mOnApplyWindowInsetsListener;
31     }
32 
33     ListenerInfo mListenerInfo;
34 
35     public void setOnTouchListener(OnTouchListener l) {
36         //在设置监听器实例时,这里会调用getListenerInfo()判断是否有ListenerInfo实例存在
37         getListenerInfo().mOnTouchListener = l;
38     }
39 
40     ListenerInfo getListenerInfo() {
41         if (mListenerInfo != null) {
42             return mListenerInfo;
43         }
44         mListenerInfo = new ListenerInfo();
45         return mListenerInfo;
46     }
mListenerInfo部分

    2、li.mOnTouchListener != null,这是判断是否有TouchListener,这是通过setOnTouchListener()设置的。

    3、(mViewFlags & ENABLED_MASK) == ENABLED,这是判断View的mViewFlags是否是ENABLED。注意:这和View是否可点击不同,可点击是CLICKABLE=true

 1     //检测是否是ENABLED,获取和设置都与mViewFlags有关
 2     public boolean isEnabled() {
 3         return (mViewFlags & ENABLED_MASK) == ENABLED;
 4     }
 5 
 6     public void setEnabled(boolean enabled) {
 7         if (enabled == isEnabled()) return;
 8 
 9         setFlags(enabled ? ENABLED : DISABLED, ENABLED_MASK);
10 
11 
12     //下面是与点击有关的获取和设置
13     public boolean isClickable() {
14         return (mViewFlags & CLICKABLE) == CLICKABLE;
15     }
16 
17     public void setClickable(boolean clickable) {
18         setFlags(clickable ? CLICKABLE : 0, CLICKABLE);
19     }
20 
21     public boolean isLongClickable() {
22         return (mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE;
23     }
24 
25     public void setLongClickable(boolean longClickable) {
26         setFlags(longClickable ? LONG_CLICKABLE : 0, LONG_CLICKABLE);
27     }
28 
29     public boolean isContextClickable() {
30         return (mViewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;
31     }
32 
33     public void setContextClickable(boolean contextClickable) {
34         setFlags(contextClickable ? CONTEXT_CLICKABLE : 0, CONTEXT_CLICKABLE);
35     }
36 
37     //注意
38     public void setOnClickListener(@Nullable OnClickListener l) {
39         if (!isClickable()) {
40             setClickable(true);
41         }
42         getListenerInfo().mOnClickListener = l;
43     }
Enable和Clickable的区别

    !!!注意:在setOnClickListener中会修改Clickable属性,即原本不能点击的View,如果添加点击的监听器,则可以点击。

    4、li.mOnTouchListener.onTouch(this, event),就是判断添加的监听事件额返回值。

    !!!只有当上面四个都是true时,设置result=true。而这个值又影响到下一个判断的执行,因为用到&&(这个操作符比较特殊,如果前一个操作数为true,则继续向后判断;如果前一个为false,则不执行后续判断直接返回false),所以前面是false这直接退出执行,否则会触发View的onTouchEvent(event)。该方法比较重要的两步见下面说明。需要注意的是:这个方法的返回值为true,则事件被消耗,否则将继续!!向后(其他的点击事件)也包括向!!父布局传递

 1 public boolean onTouchEvent(MotionEvent event) {
 2     final int viewFlags = mViewFlags;
 3 
 4     // 如果View被禁用的话,则返回它是否可以点击。
 5     if ((viewFlags & ENABLED_MASK) == DISABLED) {
 6         if (event.getAction() == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
 7             setPressed(false);
 8         }
 9         return (((viewFlags & CLICKABLE) == CLICKABLE ||
10                 (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));
11     }
12 
13     // 如果该View的mTouchDelegate不为null的话,将触摸消息分发给mTouchDelegate。
14     // mTouchDelegate的默认值是null。
15     if (mTouchDelegate != null) {
16         if (mTouchDelegate.onTouchEvent(event)) {
17             return true;
18         }
19     }
20 
21     // 如果View可以被点击的话,则执行if里面的内容。
22     // 这其中涉及到的主要是获取焦点,设置按下状态,触发onClick(), onLongClick()事件等等。
23     if (((viewFlags & CLICKABLE) == CLICKABLE ||
24             (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
25                。。。
26         }
27         return true;
28     }
29 
30     return false;
31 }    
View的onTouchEvent

    这个方法的返回值总结:

      a、该View不可用,即(viewFlags & ENABLED_MASK) == DISABLED,则返回是否可点击或长击。如果可用则向下判断

      b、mTouchDelegate如果不为null,则由其进行处理并返回结果,否则继续向下判断

      c、到这这一步,只要View可以点击或长击则返回true,否则返回false。

 下图是View事件分发的全过程图示,重点是事件首先是由顶级布局获取的,而不是其中的子View.

    

 1 package com.dqxst.first.view;
 2 
 3 import android.app.Activity;
 4 import android.os.Bundle;
 5 import android.util.Log;
 6 import android.view.MotionEvent;
 7 import android.view.View;
 8 import android.view.View.OnClickListener;
 9 import android.view.View.OnTouchListener;
10 import android.widget.Button;
11 import android.widget.ImageView;
12 import android.widget.LinearLayout;
13 
14 import com.dqxst.first.R;
15 import com.dqxst.first.myui.MyBtn;
16 
17 public class EventActivity extends Activity{
18     private final static String TAG="EventActivity";
19     private LinearLayout parent;
20     private Button btn;
21     private MyBtn myBtn;
22     private ImageView image;
23     
24     @Override
25     protected void onCreate(Bundle savedInstanceState) {
26         super.onCreate(savedInstanceState);
27         setContentView(R.layout.activity_event);
28         findView();
29         event();
30         Log.i(TAG, "enabled="+image.isEnabled());
31         Log.i(TAG, "LongClickable="+image.isLongClickable());
32     }
33 
34     private void event() {
35         parent.setOnClickListener(new OnClickListener() {
36             @Override
37             public void onClick(View v) {
38                 Log.i(TAG, "父布局的OnClickListener被触发。。。");
39             }
40         });
41         btn.setOnClickListener(new OnClickListener() {
42             @Override
43             public void onClick(View v) {
44                 Log.i(TAG, "点击btn,OnClickListener。。。");
45             }
46         });
47         btn.setOnTouchListener(new OnTouchListener() {
48             @Override
49             public boolean onTouch(View v, MotionEvent event) {
50                 Log.i(TAG, "点击btn,OnTouchListener。。。,事件action="+event.getAction());
51                 return false;
52             }
53         });
54 
55         myBtn.setOnClickListener(new OnClickListener() {
56             @Override
57             public void onClick(View v) {
58                 Log.i(TAG, "点击myBtn,OnClickListener。。。");
59             }
60         });
61         myBtn.setOnTouchListener(new OnTouchListener() {
62             @Override
63             public boolean onTouch(View v, MotionEvent event) {
64                 Log.i(TAG, "点击myBtn,OnTouchListener。。。,事件action="+event.getAction());
65                 return false;
66             }
67         });
68         image.setOnClickListener(new OnClickListener() {
69             @Override
70             public void onClick(View v) {
71                 Log.i(TAG, "点击image,OnClickListener。。。");
72             }
73         });
74         image.setOnTouchListener(new OnTouchListener() {
75             @Override
76             public boolean onTouch(View v, MotionEvent event) {
77                 Log.i(TAG, "点击image,OnTouchListener。。。,事件action="+event.getAction());
78                 return false;
79             }
80         });
81     }
82     
83     //上面已经有setOnClickListener,所以该方法不执行
84     public void btn_click(View view){
85         Log.i(TAG, "点击btn,通过xml绑定的onclick事件处理");
86     }
87 
88     private void findView() {
89         parent=(LinearLayout) findViewById(R.id.parent);
90         btn=(Button) findViewById(R.id.btn);
91         myBtn=(MyBtn) findViewById(R.id.mybtn);
92         image=(ImageView) findViewById(R.id.event_image);
93     }
94 }
事件处理的例子

参考:http://blog.csdn.net/guolin_blog/article/details/9097463http://blog.csdn.net/guolin_blog/article/details/9153747

二、MotionEvent触摸事件,这是上面事件分发之后最终在OnTouchListener()执行的事件对象。通过这个对象可以获取当前的触摸事件。

  1、单手操作:通过调用getAction()可以获取事件标识,可以和MotionEvent中定义的事件标识进行匹配。

  2、多手操作:如果需要判断多手操作需要event.getAction() & MotionEvent.ACTION_MASK来获取事件。

在MotionEvent对象中最重要的属性就是触发事件的X,Y坐标。通过getX()/getY()获取。多点触控通过getX(int pointerIndex) ,来获得对应手指事件的发生位置. 获得Y轴用getY(int pointerIndex)

参考:http://www.runoob.com/w3cnote/android-tutorial-touchlistener-ontouchevent.html

http://my.oschina.net/banxi/blog/56421

三、手势事件:其实就是android提供的封装了对MotionEvent事件的处理,把一些连续的操作响应为手势(当然也可以手动处理,但是比较麻烦)。主要用到的是GestureDetector类,使用过程分三步:

  1、创建手势监听器对象,这是通过集成/实现该类中的接口/实现类来完成的,比如OnGestureListener接口/SimpleOnGestureListener类。

  2、创建GestureDetector对象,需要通过构造器传入上面的手势监听器对象。

  3、将事件转交给该对象,通过GestureDetector.onTouch()进行事件的处理。

需要注意的是,手势事件可以针对整个activity或者其中一个View。

  1、如果针对Activity,那就不需要为其中的View添加事件监听,直接重写onTouchEvent()将事件转发即可。

  2、如果是针对特定View,那就需要给其添加OnTouchListener监听器,在该监听器中进行转发,注意:需要返回值必须为true才能正确响应

  1 package com.dqxst.first.view;
  2 
  3 import android.app.Activity;
  4 import android.os.Bundle;
  5 import android.util.Log;
  6 import android.view.GestureDetector;
  7 import android.view.GestureDetector.OnGestureListener;
  8 import android.view.MotionEvent;
  9 import android.view.View;
 10 import android.view.View.OnClickListener;
 11 import android.view.View.OnTouchListener;
 12 import android.widget.Button;
 13 import android.widget.ImageView;
 14 import android.widget.LinearLayout;
 15 
 16 import com.dqxst.first.R;
 17 import com.dqxst.first.myui.MyBtn;
 18 import com.dqxst.first.util.CommonUtils;
 19 
 20 public class EventActivity extends Activity implements OnTouchListener{
 21     private final static String TAG="EventActivity";
 22     private LinearLayout parent;
 23     private View view;
 24     private Button btn;
 25     private MyBtn myBtn;
 26     private ImageView image;
 27     
 28     private MyGestureListener mgListener;
 29     private GestureDetector mDetector;
 30 
 31     @Override
 32     protected void onCreate(Bundle savedInstanceState) {
 33         super.onCreate(savedInstanceState);
 34         setContentView(R.layout.activity_event);
 35         
 36         //实例化GestureListener与GestureDetector对象
 37         mgListener = new MyGestureListener();
 38         mDetector = new GestureDetector(this, mgListener);
 39         
 40         findView();
 41 
 42 //        image.setOnTouchListener(this);
 43     }
 44 
 45     private void findView() {
 46         parent=(LinearLayout) findViewById(R.id.parent);
 47         view=findViewById(R.id.event_test);
 48         btn=(Button) findViewById(R.id.btn);
 49         myBtn=(MyBtn) findViewById(R.id.mybtn);
 50         image=(ImageView) findViewById(R.id.event_image);
 51     }
 52 
 53     //如果是针对某一View,则使用下面的监听器,然后给相应的View进行set即可
 54     @Override
 55     public boolean onTouchEvent(MotionEvent event) {
 56         return mDetector.onTouchEvent(event);
 57     }
 58 
 59     @Override
 60 //    public boolean onTouch(View v, MotionEvent event) {
 61 //        mDetector.onTouchEvent(event);
 62 //        return true;
 63 //    }
 64     
 65     //自定义一个GestureListener,这个是View类下的,别写错哦!!!
 66     private class MyGestureListener implements OnGestureListener {
 67 
 68         @Override
 69         public boolean onDown(MotionEvent motionEvent) {
 70             Log.d(TAG, "onDown:按下");
 71             CommonUtils.toastShow(getApplicationContext(), "onDown:按下");
 72             return false;
 73         }
 74 
 75         @Override
 76         public void onShowPress(MotionEvent motionEvent) {
 77             Log.d(TAG, "onShowPress:手指按下一段时间,不过还没到长按");
 78             CommonUtils.toastShow(getApplicationContext(), "onShowPress:手指按下一段时间,不过还没到长按");
 79         }
 80 
 81         @Override
 82         public boolean onSingleTapUp(MotionEvent motionEvent) {
 83             Log.d(TAG, "onSingleTapUp:手指离开屏幕的一瞬间");
 84             CommonUtils.toastShow(getApplicationContext(), "onSingleTapUp:手指离开屏幕的一瞬间");
 85             return false;
 86         }
 87 
 88         @Override
 89         public boolean onScroll(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
 90             Log.d(TAG, "onScroll:在触摸屏上滑动");
 91             CommonUtils.toastShow(getApplicationContext(), "onScroll:在触摸屏上滑动");
 92             return false;
 93         }
 94 
 95         @Override
 96         public void onLongPress(MotionEvent motionEvent) {
 97             Log.d(TAG, "onLongPress:长按并且没有松开");
 98             CommonUtils.toastShow(getApplicationContext(), "onLongPress:长按并且没有松开");
 99         }
100 
101         @Override
102         public boolean onFling(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
103             Log.d(TAG, "onFling:迅速滑动,并松开");
104             CommonUtils.toastShow(getApplicationContext(), "onFling:迅速滑动,并松开");
105             return false;
106         }
107     }
108 
109 }
手势操作实例

上面是基本使用。除此之外还可以使用GestureOverlayView编辑手势,使用GestureLibraries(手势库)来添加(刚才编辑的手势)或者获取手势。

参考:http://www.runoob.com/w3cnote/android-tutorial-gestures.html

package com.dqxst.first.view;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.GestureDetector;
import android.view.GestureDetector.OnGestureListener;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;

import com.dqxst.first.R;
import com.dqxst.first.myui.MyBtn;
import com.dqxst.first.util.CommonUtils;

public class EventActivity extends Activity implements OnTouchListener{
    private final static String TAG="EventActivity";
    private LinearLayout parent;
    private View view;
    private Button btn;
    private MyBtn myBtn;
    private ImageView image;
    
    private MyGestureListener mgListener;
    private GestureDetector mDetector;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_event);
        
        //实例化GestureListener与GestureDetector对象
        mgListener = new MyGestureListener();
        mDetector = new GestureDetector(this, mgListener);
        
        findView();
//        event();
        
        Log.i(TAG, "enabled="+view.isEnabled());
        Log.i(TAG, "click="+view.isClickable());
        Log.i(TAG, "Longclick="+view.isLongClickable());
        image.setOnTouchListener(this);
    }

    private void event() {
        parent.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.i(TAG, "父布局的OnClickListener被触发。。。");
            }
        });
        btn.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.i(TAG, "点击btn,OnClickListener。。。");
            }
        });
        btn.setOnTouchListener(new OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                Log.i(TAG, "点击btn,OnTouchListener。。。,事件action="+event.getAction());
                return false;
            }
        });

        myBtn.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.i(TAG, "点击myBtn,OnClickListener。。。");
            }
        });
        myBtn.setOnTouchListener(new OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                Log.i(TAG, "点击myBtn,OnTouchListener。。。,事件action="+event.getAction());
                return false;
            }
        });
        image.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.i(TAG, "点击image,OnClickListener。。。");
            }
        });
        image.setOnTouchListener(new OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                Log.i(TAG, "点击image,OnTouchListener。。。,事件action="+event.getAction());
                return false;
            }
        });
    }
    
    //上面已经有setOnClickListener,所以该方法不执行
    public void btn_click(View view){
        Log.i(TAG, "点击btn,通过xml绑定的onclick事件处理");
    }

    private void findView() {
        parent=(LinearLayout) findViewById(R.id.parent);
        view=findViewById(R.id.event_test);
        btn=(Button) findViewById(R.id.btn);
        myBtn=(MyBtn) findViewById(R.id.mybtn);
        image=(ImageView) findViewById(R.id.event_image);
    }

//    @Override
//    public boolean onTouchEvent(MotionEvent event) {
//        return mDetector.onTouchEvent(event);
//    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        mDetector.onTouchEvent(event);
        return true;
    }
    
    //自定义一个GestureListener,这个是View类下的,别写错哦!!!
    private class MyGestureListener implements OnGestureListener {

        @Override
        public boolean onDown(MotionEvent motionEvent) {
            Log.d(TAG, "onDown:按下");
            CommonUtils.toastShow(getApplicationContext(), "onDown:按下");
            return false;
        }

        @Override
        public void onShowPress(MotionEvent motionEvent) {
            Log.d(TAG, "onShowPress:手指按下一段时间,不过还没到长按");
            CommonUtils.toastShow(getApplicationContext(), "onShowPress:手指按下一段时间,不过还没到长按");
        }

        @Override
        public boolean onSingleTapUp(MotionEvent motionEvent) {
            Log.d(TAG, "onSingleTapUp:手指离开屏幕的一瞬间");
            CommonUtils.toastShow(getApplicationContext(), "onSingleTapUp:手指离开屏幕的一瞬间");
            return false;
        }

        @Override
        public boolean onScroll(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
            Log.d(TAG, "onScroll:在触摸屏上滑动");
            CommonUtils.toastShow(getApplicationContext(), "onScroll:在触摸屏上滑动");
            return false;
        }

        @Override
        public void onLongPress(MotionEvent motionEvent) {
            Log.d(TAG, "onLongPress:长按并且没有松开");
            CommonUtils.toastShow(getApplicationContext(), "onLongPress:长按并且没有松开");
        }

        @Override
        public boolean onFling(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
            Log.d(TAG, "onFling:迅速滑动,并松开");
            CommonUtils.toastShow(getApplicationContext(), "onFling:迅速滑动,并松开");
            return false;
        }
    }

}

原文地址:https://www.cnblogs.com/songfeilong2325/p/5422593.html