Android的GestureDetector类
来源:互联网 发布:淘宝卖家违规 编辑:程序博客网 时间:2024/05/20 02:23
最近在做一个小需求的时候,会用到手势的判断,在对应不同的事件。用户在触摸手机屏幕时,会产生很多不同的手势,比如点击、双击、长按、拖拽、抛。
OnTouchListener
刚开始我使用的方式是让类继承OnTouchListener内部接口,然后重写onTouch(View v,MotionEvent event)方法,然后处理一些touch事件,虽然看起来貌似解决了目前的问题,但是这种方式太简单了。如果要处理负责的手势,用这个接口去判断手势会非常麻烦。
我从触屏的时间和触屏的滑动长度来判别手势,这种自定义的方式增加了代码的复杂度而且不准确。不过,我相信GestureDetector类的包装类中应该封装了以上思想的实现,可能它的数据是建立在实验的基础上。
代码如下:
public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: pressStartTime = System.currentTimeMillis(); pressedX = event.getX(); pressedY = event.getY(); break; case MotionEvent.ACTION_UP: long pressDuration = System.currentTimeMillis() - pressStartTime; /** * 判断touch是点击还是滑动,点击,触发以下事件 */ if (pressDuration < MAX_CLICK_DURATION && distance(pressedX, pressedY, event.getX(), event.getY(), getActivity()) < MAX_CLICK_DISTANCE) { .......... } default: break; } return false; }
GestureDetector类
OnGestureListener有下面的几个动作:
- 按下(onDown): 刚刚手指接触到触摸屏的那一刹那,就是触的那一下。
- 抛掷(onFling): 手指在触摸屏上迅速移动,并松开的动作。
- 长按(onLongPress): 手指按在持续一段时间,并且没有松开。
- 滚动(onScroll): 手指在触摸屏上滑动。
- 按住(onShowPress): 手指按在触摸屏上,它的时间范围在按下起效,在长按之前。
- 抬起(onSingleTapUp):手指离开触摸屏的那一刹那。
GestureDetector.OnGestureListener接口:用来通知普通的手势事件,该接口有如下六个回调函数:
1. onDown(MotionEvent e):按下事件;
2. onSingleTapUp(MotionEvent e):单击时,在按下后既没有滑动(onScroll),又没有长按(onLongPress),然后抬起时触发。
点击一下非常快的(不滑动)Touchup:onDown->onSingleTapUp->onSingleTapConfirmed
点击一下稍微慢点的(不滑动)Touchup:onDown->onShowPress->onSingleTapUp->onSingleTapConfirmed
3. onShowPress(MotionEvent e):down事件发生而move或up还没发生前,触发该事件;Touch了但还没有滑动时触发。
与onDown,onLongPress比较:onDown只要按下一定立刻触发。而按下后过一会没有滑动先触发onShowPress再是onLongPress。如,按下后一直不滑动,触发顺序onDown–>onShowPress–>onLongPress。
4. onLongPress(MotionEvent e):长按事件;Touch了不移动一直Touch down时触发。
5. onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY):滑动手势事件;Touch了滑动一点距离后,在抬起时才会触发。
参数:e1,第1个ACTION_DOWN MotionEvent 并且只有一个;e2,最后一个ACTION_MOVE MotionEvent ;velocityX,X轴上的移动速度(像素/秒);velocityY,Y轴上的移动速度(像素/秒)。
触发条件:X轴的坐标位移大于FLING_MIN_DISTANCE,且移动速度大于FLING_MIN_VELOCITY个像素/秒时。
6. onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY):在屏幕上拖动事件。在ACTION_MOVE动作发生时触发,会多次触发。onDown–>onScroll–>onScroll–>onFiling
SimpleOnGestureListener类是GestureDetector提供给我们的一个更方便的响应不同手势的类,这个类实现了上述两个接口(但是所有的方法体都是空的),该类是static class,也就是说它实际上是一个外部类。可以在外部继承这个类,重写里面的手势处理方法。——以上部分摘抄自链接中的博客
读源码
在GestureDetector类中果然也有个onTouchEvent方法,我们来读一下源码,看看别人是怎么实现的。
看源码之前,我们再了解一些概念:
- ACTION_MASK在Android中应用于多点触摸操作。字面的意思是动作掩码
- ACTION_DOWN和ACTION_UP是单点触摸屏幕,按下去和放开的操作;
- ACTION_POINTER_DOWN和ACTION_POINTER_UP是多点触摸屏幕,当有一只手指按下去的时候,另一只手指按下和放开的动作捕获。
- ACTION_MOVE是手指在屏幕上移动的操作。
使用event.getAction()&MotionEvent.ACTION_MASK就可以处理多点触摸的ACTION_POINTER_DOWN和ACTION_POINTER_UP事件。
感觉明白了那么一点点,我们再做一个实验。
intACTION_MASK=0xff;
int ACTION_DOWN= 0;
int ACTION_UP= 1;
int ACTION_MOVE= 2;
int ACTION_POINTER_DOWN= 5;
int ACTION_POINTER_UP= 6;
主要是来搞清楚ACTION_POINTER_DOWN和ACTION_POINTER_UP的具体动作。
我们定义一个Activity,在里面重写一下OnTouchEvent方法,然后触发不同的触摸事件,输出相应的结果。
@Override public boolean onTouchEvent(MotionEvent event) { int action = event.getAction(); int result=action & MotionEvent.ACTION_MASK; switch (result) { case MotionEvent.ACTION_DOWN: showMsg("ACTION_DOWN"+"------" + action+"------" + result); break; case MotionEvent.ACTION_UP: showMsg("ACTION_UP" + "------" + action+"------" + result); break; case MotionEvent.ACTION_POINTER_UP: showMsg("ACTION_POINTER_UP"+ "------" + action+"------" + result); break; case MotionEvent.ACTION_POINTER_DOWN: showMsg("ACTION_POINTER_DOWN"+ "------" + action+"------" + result); break; } return super.onTouchEvent(event); } private void showMsg(String str) { Log.d("Action",str); }
运行结果如下所示
屏幕上触发的事件是:
单指按下—》单指拿起
单指按下—》第二根手指按下—》第二根手指拿起—》第一根手指拿起
单指按下—》第二根手指按下—》第三根手指按下—》第三根手指拿起—》第二根手指拿起—》第一根手指拿起
双指按下—》双指拿起
综上所述:只有但多个(大于1)手指放下拿起才会触发ACTION_POINTER_DOWN和ACTION_POINTER_UP事件
velocityTracker简介参考链接:http://blog.csdn.net/hudashi/article/details/7352157
再看源码:
public boolean onTouchEvent(MotionEvent ev) {//传入的是当前的手势事件 if (mInputEventConsistencyVerifier != null) { //用于调试目的的一致性校验,这个我们不用管 mInputEventConsistencyVerifier.onTouchEvent(ev, 0); } final int action = ev.getAction(); //获得手势事件 if (mVelocityTracker == null) {//用于跟踪触摸事件的速度 mVelocityTracker = VelocityTracker.obtain(); } //判断是否是多点触摸,多于1个触点中是否有手指拿起的 //ACTION_POINTER_UP的值为6,我们可以看到上图gif中哪些动作的result是6 mVelocityTracker.addMovement(ev); final boolean pointerUp = (action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_UP; //表示触点的Index,第一个手指按下是0,第二个按下是1,即屏幕上剩余触点 final int skipIndex = pointerUp ? ev.getActionIndex() : -1; // Determine focal point float sumX = 0, sumY = 0; ////触摸点的个数 final int count = ev.getPointerCount(); for (int i = 0; i < count; i++) { if (skipIndex == i) continue;//单点就跳过,多点就计算触摸点x,y的和 sumX += ev.getX(i); sumY += ev.getY(i); } final int div = pointerUp ? count - 1 : count; final float focusX = sumX / div;//计算多个触点x,y的平均值,即为焦点值 final float focusY = sumY / div; boolean handled = false; switch (action & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_POINTER_DOWN://屏幕上已有触点,再添加触点,记录焦点的位置,取消长按事件和点击事件 mDownFocusX = mLastFocusX = focusX; mDownFocusY = mLastFocusY = focusY; // Cancel long press and taps cancelTaps(); break; case MotionEvent.ACTION_POINTER_UP://减少触点,屏幕上还有触点,记录焦点的位置 mDownFocusX = mLastFocusX = focusX; mDownFocusY = mLastFocusY = focusY; // Check the dot product of current velocities. //减少触点,屏幕上还有触点,记录焦点的位置 // If the pointer that left was opposing another velocity vector, clear. mVelocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity); final int upIndex = ev.getActionIndex(); final int id1 = ev.getPointerId(upIndex); final float x1 = mVelocityTracker.getXVelocity(id1); final float y1 = mVelocityTracker.getYVelocity(id1); for (int i = 0; i < count; i++) { if (i == upIndex) continue; final int id2 = ev.getPointerId(i); final float x = x1 * mVelocityTracker.getXVelocity(id2); final float y = y1 * mVelocityTracker.getYVelocity(id2); final float dot = x + y; if (dot < 0) { mVelocityTracker.clear(); break; } } break; case MotionEvent.ACTION_DOWN: if (mDoubleTapListener != null) { boolean hadTapMessage = mHandler.hasMessages(TAP); if (hadTapMessage) mHandler.removeMessages(TAP); if ((mCurrentDownEvent != null) && (mPreviousUpEvent != null) && hadTapMessage && isConsideredDoubleTap(mCurrentDownEvent, mPreviousUpEvent, ev)) { // This is a second tap mIsDoubleTapping = true; // Give a callback with the first tap of the double-tap handled |= mDoubleTapListener.onDoubleTap(mCurrentDownEvent); // Give a callback with down event of the double-tap handled |= mDoubleTapListener.onDoubleTapEvent(ev); } else { // This is a first tap mHandler.sendEmptyMessageDelayed(TAP, DOUBLE_TAP_TIMEOUT); } } mDownFocusX = mLastFocusX = focusX; mDownFocusY = mLastFocusY = focusY; if (mCurrentDownEvent != null) { mCurrentDownEvent.recycle(); } mCurrentDownEvent = MotionEvent.obtain(ev); mAlwaysInTapRegion = true; mAlwaysInBiggerTapRegion = true; mStillDown = true; mInLongPress = false; mDeferConfirmSingleTap = false; if (mIsLongpressEnabled) { mHandler.removeMessages(LONG_PRESS); mHandler.sendEmptyMessageAtTime(LONG_PRESS, mCurrentDownEvent.getDownTime() + TAP_TIMEOUT + LONGPRESS_TIMEOUT); } mHandler.sendEmptyMessageAtTime(SHOW_PRESS, mCurrentDownEvent.getDownTime() + TAP_TIMEOUT); handled |= mListener.onDown(ev); break; case MotionEvent.ACTION_MOVE: if (mInLongPress) { break; } final float scrollX = mLastFocusX - focusX; final float scrollY = mLastFocusY - focusY; if (mIsDoubleTapping) { // Give the move events of the double-tap handled |= mDoubleTapListener.onDoubleTapEvent(ev); } else if (mAlwaysInTapRegion) { final int deltaX = (int) (focusX - mDownFocusX); final int deltaY = (int) (focusY - mDownFocusY); int distance = (deltaX * deltaX) + (deltaY * deltaY); if (distance > mTouchSlopSquare) { handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY); mLastFocusX = focusX; mLastFocusY = focusY; mAlwaysInTapRegion = false; mHandler.removeMessages(TAP); mHandler.removeMessages(SHOW_PRESS); mHandler.removeMessages(LONG_PRESS); } if (distance > mDoubleTapTouchSlopSquare) { mAlwaysInBiggerTapRegion = false; } } else if ((Math.abs(scrollX) >= 1) || (Math.abs(scrollY) >= 1)) { handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY); mLastFocusX = focusX; mLastFocusY = focusY; } break; case MotionEvent.ACTION_UP: mStillDown = false; MotionEvent currentUpEvent = MotionEvent.obtain(ev); if (mIsDoubleTapping) { // Finally, give the up event of the double-tap handled |= mDoubleTapListener.onDoubleTapEvent(ev); } else if (mInLongPress) { mHandler.removeMessages(TAP); mInLongPress = false; } else if (mAlwaysInTapRegion) { handled = mListener.onSingleTapUp(ev); if (mDeferConfirmSingleTap && mDoubleTapListener != null) { mDoubleTapListener.onSingleTapConfirmed(ev); } } else { // A fling must travel the minimum tap distance final VelocityTracker velocityTracker = mVelocityTracker; final int pointerId = ev.getPointerId(0); velocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity); final float velocityY = velocityTracker.getYVelocity(pointerId); final float velocityX = velocityTracker.getXVelocity(pointerId); if ((Math.abs(velocityY) > mMinimumFlingVelocity) || (Math.abs(velocityX) > mMinimumFlingVelocity)){ handled = mListener.onFling(mCurrentDownEvent, ev, velocityX, velocityY); } } if (mPreviousUpEvent != null) { mPreviousUpEvent.recycle(); } // Hold the event we obtained above - listeners may have changed the original. mPreviousUpEvent = currentUpEvent; if (mVelocityTracker != null) { // This may have been cleared when we called out to the // application above. mVelocityTracker.recycle(); mVelocityTracker = null; } mIsDoubleTapping = false; mDeferConfirmSingleTap = false; mHandler.removeMessages(SHOW_PRESS); mHandler.removeMessages(LONG_PRESS); break; case MotionEvent.ACTION_CANCEL: cancel(); break; } if (!handled && mInputEventConsistencyVerifier != null) { mInputEventConsistencyVerifier.onUnhandledEvent(ev, 0); } return handled; }
- Android的GestureDetector类
- Android手势监听类GestureDetector的使用
- Android手势监听类GestureDetector的使用
- Android手势监听类GestureDetector的使用
- Android手势监听类GestureDetector的使用
- Android手势监听类GestureDetector的使用
- android的GestureDetector详解
- Android的GestureDetector
- android.view.GestureDetector类
- GestureDetector类的用法
- GestureDetector类的用法
- GestureDetector类的用法
- GestureDetector类的用法
- GestureDetector类的用法
- GestureDetector类的用法
- GestureDetector类的用法
- GestureDetector类的用法
- GestureDetector类的用法
- Struts2 请求参数接收
- CorePlot1.0:柱状图
- C ++ 输入输出流
- “大部人从外界看阿里是‘雾里看花’”,所以马云亲自敲出了这5000字的致股东信
- 【Cocos2dx】精灵触摸跳跃功能
- Android的GestureDetector类
- Android Studio导入工程时出现的几种错误
- Python简介
- 每日思维行为记录,宏观模型 2015-10-09
- listView和Adapter
- C语言经典笔试题(一)
- iOS中获取手机截屏的实现 (第二种方法)
- LinearLayout和RelativeLayout 比较
- jpg图片转ico图片格式