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;    }
0 0
原创粉丝点击