学习GestureDetector中的点击事件的实现方式

来源:互联网 发布:浏览器打开淘宝很慢 编辑:程序博客网 时间:2024/05/13 03:29

   本周项目需要做一个简单的功能就是双击图片放大,这是一个简单的功能,网上一搜有很多,大多都是使用的GestureDetector的listener进行监听,但是自己看了一下GestureDetector的代码决定自己来实现,首先是先查看GestureDetector的ontouch代码如下:由于只涉及到MotionEvent.ACTION_DOWN:和MotionEvent.ACTION_MOVE:,因此现在只讨论这两个方法。

 

public boolean onTouchEvent(MotionEvent ev) {
        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onTouchEvent(ev, 0);
        }


        final int action = ev.getAction();
        final float y = ev.getY();
        final float x = ev.getX();


        if (mVelocityTracker == null) {
            mVelocityTracker = VelocityTracker.obtain();
        }
        mVelocityTracker.addMovement(ev);


        boolean handled = false;


        switch (action & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_POINTER_DOWN:
            if (mIgnoreMultitouch) {
                // Multitouch event - abort.
                cancel();
            }
            break;


        case MotionEvent.ACTION_POINTER_UP:
            // Ending a multitouch gesture and going back to 1 finger
            if (mIgnoreMultitouch && ev.getPointerCount() == 2) {
                int index = (((action & MotionEvent.ACTION_POINTER_INDEX_MASK)
                        >> MotionEvent.ACTION_POINTER_INDEX_SHIFT) == 0) ? 1 : 0;
                mLastMotionX = ev.getX(index);
                mLastMotionY = ev.getY(index);
                mVelocityTracker.recycle();
                mVelocityTracker = VelocityTracker.obtain();
            }
            break;


        case MotionEvent.ACTION_DOWN:
            if (mDoubleTapListener != null) {
                            //是否有以前触发的点击事件,如果有点击事件就将其移除
                boolean hadTapMessage = mHandler.hasMessages(TAP);
                if (hadTapMessage) mHandler.removeMessages(TAP);
                             //判断是否符合双击的条件,根据hadTapMessage来判断当前是第二次点击
                if ((mCurrentDownEvent != null) && (mPreviousUpEvent != null) && hadTapMessage &&
                        isConsideredDoubleTap(mCurrentDownEvent, mPreviousUpEvent, ev)) {
                            //如果符合就将mIsDoubleTapping设置为true,然后触发onDoubleTap和onDoubleTapEvent事件
                    // 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 如果不符合双击的条件,就触发单击事件注意DOUBLE_TAP_TIMEOUT这个很重要
                    mHandler.sendEmptyMessageDelayed(TAP, DOUBLE_TAP_TIMEOUT);
                }
            }


            mLastMotionX = x;
            mLastMotionY = y;
            if (mCurrentDownEvent != null) {
                mCurrentDownEvent.recycle();
            }
//设置为当前的事件
            mCurrentDownEvent = MotionEvent.obtain(ev);
//初始设置mAlwaysInTapRegion(是否一直点击同一个位置 ),mAlwaysInBiggerTapRegion(是否在更大的范围内点击)和mStillDown(是否按下就不动了)都为true:
            mAlwaysInTapRegion = true;
            mAlwaysInBiggerTapRegion = true;
            mStillDown = true;
            mInLongPress = 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 || (mIgnoreMultitouch && ev.getPointerCount() > 1)) {
                break;
            }
            final float scrollX = mLastMotionX - x;
            final float scrollY = mLastMotionY - y;
            if (mIsDoubleTapping) {
                // Give the move events of the double-tap  如果是双击事件触发onDoubleTapEvent
                handled |= mDoubleTapListener.onDoubleTapEvent(ev);
            } else if (mAlwaysInTapRegion) {
                final int deltaX = (int) (x - mCurrentDownEvent.getX());
                final int deltaY = (int) (y - mCurrentDownEvent.getY());
                int distance = (deltaX * deltaX) + (deltaY * deltaY);
//如果是在点击同一个位置,并且符合滑动的距离,触发onscroll事件
                if (distance > mTouchSlopSquare) {
                    handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
                    mLastMotionX = x;
                    mLastMotionY = y;
                    mAlwaysInTapRegion = false;
                    mHandler.removeMessages(TAP);
                    mHandler.removeMessages(SHOW_PRESS);
                    mHandler.removeMessages(LONG_PRESS);
                }
                if (distance > mBiggerTouchSlopSquare) {
   //对mAlwaysInBiggerTapRegion进行重新定义
                    mAlwaysInBiggerTapRegion = false;
                }
            } else if ((Math.abs(scrollX) >= 1) || (Math.abs(scrollY) >= 1)) {
   //如果现在一直在滑动中,触发onscroll事件
                handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
                mLastMotionX = x;
                mLastMotionY = y;
            }
            break;


        case MotionEvent.ACTION_UP:
            mStillDown = false;
            MotionEvent currentUpEvent = MotionEvent.obtain(ev);
            if (mIsDoubleTapping) {
                // Finally, give the up event of the double-tap  如果是双击触发onDoubleTapEvent事件
                handled |= mDoubleTapListener.onDoubleTapEvent(ev);
            } else if (mInLongPress) {
                mHandler.removeMessages(TAP);
                mInLongPress = false;
            } else if (mAlwaysInTapRegion) {
                handled = mListener.onSingleTapUp(ev);
            } else {


   //判断是不是要触发onFling方法
                // A fling must travel the minimum tap distance
                final VelocityTracker velocityTracker = mVelocityTracker;
                velocityTracker.computeCurrentVelocity(1000, mMaximumFlingVelocity);
                final float velocityY = velocityTracker.getYVelocity();
                final float velocityX = velocityTracker.getXVelocity();


                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;
            mVelocityTracker.recycle();
            mVelocityTracker = null;
            mIsDoubleTapping = 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;

    }


下面看handler的方法:

    private class GestureHandler extends Handler {
        GestureHandler() {
            super();
        }


        GestureHandler(Handler handler) {
            super(handler.getLooper());
        }


        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case SHOW_PRESS:
                mListener.onShowPress(mCurrentDownEvent);
                break;
                
            case LONG_PRESS:
                dispatchLongPress();
                break;
                
            case TAP:
                // If the user's finger is still down, do not count it as a tap  判断是否要触发点击事件
                if (mDoubleTapListener != null && !mStillDown) {
                    mDoubleTapListener.onSingleTapConfirmed(mCurrentDownEvent);
                }
                break;
            default:
                throw new RuntimeException("Unknown message " + msg); //never
            }
        }
    }

//判断双击的条件
  private boolean isConsideredDoubleTap(MotionEvent firstDown, MotionEvent firstUp,
            MotionEvent secondDown) {
        if (!mAlwaysInBiggerTapRegion) {
            return false;
        }
        if (secondDown.getEventTime() - firstUp.getEventTime() > DOUBLE_TAP_TIMEOUT) {
            return false;
        }
        int deltaX = (int) firstDown.getX() - (int) secondDown.getX();
        int deltaY = (int) firstDown.getY() - (int) secondDown.getY();
        return (deltaX * deltaX + deltaY * deltaY < mDoubleTapSlopSquare);
    }
  双击事件的原理是:首先在down按下的时候,查找是否有上次没有消耗的点击事件,如果有取消,同时判断是否符合双击的条件(时间上和位置上)。通俗地讲就是如果第二次点击在DOUBLE_TAP_TIMEOUT的
时间的内点击,此时就会取消上次的点击message,同时根据条件判定为双击事件;
单击事件;两次点击的时间间隔超过DOUBLE_TAP_TIMEOUT即可;
长按事件:第一次down的时候出发长按事件但是mHandler.sendEmptyMessageAtTime(LONG_PRESS, mCurrentDownEvent.getDownTime()+ TAP_TIMEOUT + LONGPRESS_TIMEOUT);,但是实在
mCurrentDownEvent.getDownTime()+ TAP_TIMEOUT + LONGPRESS_TIMEOUT时间后开始发送消息,同时分别在move和up事件里面进行判断,如果分别出发了里面的事件,就将长按事件清除;
滑动事件:是在up里面出发,比较简单不做讲述

在学习的时候看了一下http://blog.csdn.net/lonelyroamer/article/details/7558313这篇文章,在此表示感谢

原创粉丝点击