一、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 }
对于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,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 }
上面可以看到:这个方法是对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 }
上面还需注意的就是第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 }
上面是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 }
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 }
!!!注意:在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 }
这个方法的返回值总结:
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/9097463,http://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;
}
}
}